From 9fe20779c227c73b47300a9feaffa756e65a8c71 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 25 Jan 2019 11:07:24 -0700 Subject: [PATCH] Fixed issues in Enable Metadata Source tab --- .../resources/i18n/messages_en.properties | 4 + ...n.action.ts => admin-collection.action.ts} | 8 -- .../action/metadata-collection.action.ts | 90 ++++++++++++ ui/src/app/admin/admin.component.spec.ts | 12 +- ui/src/app/admin/admin.component.ts | 12 +- ui/src/app/admin/admin.module.ts | 5 +- .../component/enable-metadata.component.html | 5 +- .../component/enable-metadata.component.ts | 31 +++-- .../component/user-management.component.ts | 4 +- .../container/action-required.component.ts | 5 +- .../container/admin-management.component.ts | 6 +- ...n.effect.ts => admin-collection.effect.ts} | 13 +- .../effect/metadata-collection.effect.ts | 128 ++++++++++++++++++ ...ec.ts => admin-collection.reducer.spec.ts} | 6 +- ...reducer.ts => admin-collection.reducer.ts} | 2 +- ui/src/app/admin/reducer/index.spec.ts | 40 ++++++ ui/src/app/admin/reducer/index.ts | 50 +++++-- .../metadata-collection.reducer.spec.ts | 72 ++++++++++ .../reducer/metadata-collection.reducer.ts | 59 ++++++++ ui/src/app/core/model/user.ts | 9 +- ui/src/app/core/reducer/user.reducer.spec.ts | 18 +-- .../container/dashboard.component.ts | 1 - .../domain/service/resolver.service.ts | 11 +- .../component/entity-item.component.ts | 3 + .../component/resolver-item.component.html | 26 +++- .../component/resolver-item.component.ts | 5 +- .../dashboard-resolvers-list.component.html | 3 +- .../resolver/effect/collection.effects.ts | 30 ++-- .../resolver/reducer/collection.reducer.ts | 12 ++ ui/src/app/shared/util.ts | 3 +- 30 files changed, 564 insertions(+), 109 deletions(-) rename ui/src/app/admin/action/{user-collection.action.ts => admin-collection.action.ts} (94%) create mode 100644 ui/src/app/admin/action/metadata-collection.action.ts rename ui/src/app/admin/effect/{user-collection.effect.ts => admin-collection.effect.ts} (88%) create mode 100644 ui/src/app/admin/effect/metadata-collection.effect.ts rename ui/src/app/admin/reducer/{user-collection.reducer.spec.ts => admin-collection.reducer.spec.ts} (93%) rename ui/src/app/admin/reducer/{user-collection.reducer.ts => admin-collection.reducer.ts} (97%) create mode 100644 ui/src/app/admin/reducer/index.spec.ts create mode 100644 ui/src/app/admin/reducer/metadata-collection.reducer.spec.ts create mode 100644 ui/src/app/admin/reducer/metadata-collection.reducer.ts diff --git a/backend/src/main/resources/i18n/messages_en.properties b/backend/src/main/resources/i18n/messages_en.properties index 7a1efb615..81af2f217 100644 --- a/backend/src/main/resources/i18n/messages_en.properties +++ b/backend/src/main/resources/i18n/messages_en.properties @@ -387,6 +387,10 @@ label.role=Role label.delete=Delete? label.delete-request=Delete Request +label.enable=Enable +label.disable=Disable +label.enable-metadata-sources=Enable Metadata Sources + message.delete-user-title=Delete User? message.delete-user-body=You are requesting to delete a user. If you complete this process the user will be removed. This cannot be undone. Do you wish to continue? diff --git a/ui/src/app/admin/action/user-collection.action.ts b/ui/src/app/admin/action/admin-collection.action.ts similarity index 94% rename from ui/src/app/admin/action/user-collection.action.ts rename to ui/src/app/admin/action/admin-collection.action.ts index 8c9c3e420..96c2ae937 100644 --- a/ui/src/app/admin/action/user-collection.action.ts +++ b/ui/src/app/admin/action/admin-collection.action.ts @@ -11,7 +11,6 @@ export enum AdminCollectionActionTypes { UPDATE_ADMIN_SUCCESS = '[Admin Collection] Update Admin Success', UPDATE_ADMIN_FAIL = '[Admin Collection] Update Admin Fail', - LOAD_NEW_USERS_REQUEST = '[Admin Collection] Load New Users Request', LOAD_ADMIN_REQUEST = '[Admin Collection] Load Admin Request', LOAD_ADMIN_SUCCESS = '[Admin Collection] Load Admin Success', LOAD_ADMIN_ERROR = '[Admin Collection] Load Admin Error', @@ -52,12 +51,6 @@ export class LoadAdminRequest implements Action { constructor() { } } -export class LoadNewUsersRequest implements Action { - readonly type = AdminCollectionActionTypes.LOAD_NEW_USERS_REQUEST; - - constructor() { } -} - export class LoadAdminSuccess implements Action { readonly type = AdminCollectionActionTypes.LOAD_ADMIN_SUCCESS; @@ -133,7 +126,6 @@ export type AdminCollectionActionsUnion = | LoadAdminRequest | LoadAdminSuccess | LoadAdminError - | LoadNewUsersRequest | AddAdminRequest | AddAdminSuccess | AddAdminFail diff --git a/ui/src/app/admin/action/metadata-collection.action.ts b/ui/src/app/admin/action/metadata-collection.action.ts new file mode 100644 index 000000000..01795d49b --- /dev/null +++ b/ui/src/app/admin/action/metadata-collection.action.ts @@ -0,0 +1,90 @@ +import { Action } from '@ngrx/store'; +import { MetadataResolver } from '../../metadata/domain/model'; +import { Update } from '@ngrx/entity'; + +export enum MetadataCollectionActionTypes { + UPDATE_METADATA_REQUEST = '[Admin Metadata Collection] Update Request', + UPDATE_METADATA_SUCCESS = '[Admin Metadata Collection] Update Success', + UPDATE_METADATA_FAIL = '[Admin Metadata Collection] Update Fail', + UPDATE_METADATA_CONFLICT = '[Admin Metadata Collection] Update Conflict', + + LOAD_METADATA_REQUEST = '[Admin Metadata Collection] Load Metadata REQUEST', + LOAD_METADATA_SUCCESS = '[Admin Metadata Collection] Load Metadata SUCCESS', + LOAD_METADATA_ERROR = '[Admin Metadata Collection] Load Metadata ERROR', + + REMOVE_METADATA = '[Admin Metadata Collection] Remove Metadata', + REMOVE_METADATA_SUCCESS = '[Admin Metadata Collection] Remove Metadata Success', + REMOVE_METADATA_FAIL = '[Admin Metadata Collection] Remove Metadata Fail', +} + +export class LoadMetadataRequest implements Action { + readonly type = MetadataCollectionActionTypes.LOAD_METADATA_REQUEST; + + constructor() { } +} + +export class LoadMetadataSuccess implements Action { + readonly type = MetadataCollectionActionTypes.LOAD_METADATA_SUCCESS; + + constructor(public payload: MetadataResolver[]) { } +} + +export class LoadMetadataError implements Action { + readonly type = MetadataCollectionActionTypes.LOAD_METADATA_ERROR; + + constructor(public payload: any) { } +} + +export class UpdateMetadataRequest implements Action { + readonly type = MetadataCollectionActionTypes.UPDATE_METADATA_REQUEST; + + constructor(public payload: MetadataResolver) { } +} + +export class UpdateMetadataSuccess implements Action { + readonly type = MetadataCollectionActionTypes.UPDATE_METADATA_SUCCESS; + + constructor(public payload: Update) { } +} + +export class UpdateMetadataFail implements Action { + readonly type = MetadataCollectionActionTypes.UPDATE_METADATA_FAIL; + + constructor(public payload: any) { } +} + +export class UpdateMetadataConflict implements Action { + readonly type = MetadataCollectionActionTypes.UPDATE_METADATA_CONFLICT; + + constructor(public payload: MetadataResolver) { } +} + +export class RemoveMetadataRequest implements Action { + readonly type = MetadataCollectionActionTypes.REMOVE_METADATA; + + constructor(public payload: MetadataResolver) { } +} + +export class RemoveMetadataSuccess implements Action { + readonly type = MetadataCollectionActionTypes.REMOVE_METADATA_SUCCESS; + + constructor(public payload: MetadataResolver) { } +} + +export class RemoveMetadataFail implements Action { + readonly type = MetadataCollectionActionTypes.REMOVE_METADATA_FAIL; + + constructor(public payload: MetadataResolver) { } +} + +export type MetadataCollectionActionsUnion = + | LoadMetadataRequest + | LoadMetadataSuccess + | LoadMetadataError + | RemoveMetadataRequest + | RemoveMetadataSuccess + | RemoveMetadataFail + | UpdateMetadataRequest + | UpdateMetadataSuccess + | UpdateMetadataFail + | UpdateMetadataConflict; diff --git a/ui/src/app/admin/admin.component.spec.ts b/ui/src/app/admin/admin.component.spec.ts index 4cb5cb3e0..909e20c03 100644 --- a/ui/src/app/admin/admin.component.spec.ts +++ b/ui/src/app/admin/admin.component.spec.ts @@ -1,21 +1,30 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { AdminComponent } from './admin.component'; import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, combineReducers, Store } from '@ngrx/store'; +import * as fromAdmin from './reducer'; describe('Admin Root Component', () => { let fixture: ComponentFixture; let instance: AdminComponent; + let store: Store; beforeEach(() => { TestBed.configureTestingModule({ imports: [ - RouterTestingModule + RouterTestingModule, + StoreModule.forRoot({ + admin: combineReducers(fromAdmin.reducers) + }) ], declarations: [ AdminComponent ], }); + store = TestBed.get(Store); + spyOn(store, 'dispatch'); + fixture = TestBed.createComponent(AdminComponent); instance = fixture.componentInstance; }); @@ -24,5 +33,6 @@ describe('Admin Root Component', () => { fixture.detectChanges(); expect(fixture).toBeDefined(); + expect(store.dispatch).toHaveBeenCalled(); }); }); diff --git a/ui/src/app/admin/admin.component.ts b/ui/src/app/admin/admin.component.ts index 6d95d1d23..f4670889d 100644 --- a/ui/src/app/admin/admin.component.ts +++ b/ui/src/app/admin/admin.component.ts @@ -1,4 +1,8 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; + +import * as fromRoot from '../app.reducer'; +import { Store } from '@ngrx/store'; +import { LoadAdminRequest } from './action/admin-collection.action'; @Component({ selector: 'admin-page', @@ -6,5 +10,9 @@ import { Component, OnInit } from '@angular/core'; styleUrls: [] }) export class AdminComponent { - constructor() { } + constructor( + private store: Store + ) { + this.store.dispatch(new LoadAdminRequest()); + } } diff --git a/ui/src/app/admin/admin.module.ts b/ui/src/app/admin/admin.module.ts index 6ccd53c87..fd25105a4 100644 --- a/ui/src/app/admin/admin.module.ts +++ b/ui/src/app/admin/admin.module.ts @@ -11,7 +11,7 @@ import { AdminManagementPageComponent } from './container/admin-management.compo import { AdminComponent } from './admin.component'; import { reducers } from './reducer'; import { AdminService } from './service/admin.service'; -import { AdminCollectionEffects } from './effect/user-collection.effect'; +import { AdminCollectionEffects } from './effect/admin-collection.effect'; import { EffectsModule } from '@ngrx/effects'; import { DeleteUserDialogComponent } from './component/delete-user-dialog.component'; import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap'; @@ -20,6 +20,7 @@ import { AccessRequestComponent } from './component/access-request.component'; import { UserManagementComponent } from './component/user-management.component'; import { EnableMetadataComponent } from './component/enable-metadata.component'; import { ManagerModule } from '../metadata/manager/manager.module'; +import { MetadataCollectionEffects } from './effect/metadata-collection.effect'; @NgModule({ declarations: [ @@ -38,7 +39,7 @@ import { ManagerModule } from '../metadata/manager/manager.module'; CommonModule, I18nModule, StoreModule.forFeature('admin', reducers), - EffectsModule.forFeature([AdminCollectionEffects]), + EffectsModule.forFeature([AdminCollectionEffects, MetadataCollectionEffects]), FormsModule, RouterModule, HttpClientModule, diff --git a/ui/src/app/admin/component/enable-metadata.component.html b/ui/src/app/admin/component/enable-metadata.component.html index 6ab978649..cba54f177 100644 --- a/ui/src/app/admin/component/enable-metadata.component.html +++ b/ui/src/app/admin/component/enable-metadata.component.html @@ -8,7 +8,10 @@ (select)="edit(resolver)" (toggle)="toggleEntity(resolver)" (preview)="openPreviewDialog(resolver)" - (delete)="deleteResolver(resolver)"> + (delete)="deleteResolver(resolver)" + (toggleEnabled)="toggleResolverEnabled(resolver, !resolver.enabled)" + [showAdminFunctions]="true" + [allowDelete]="true"> \ No newline at end of file diff --git a/ui/src/app/admin/component/enable-metadata.component.ts b/ui/src/app/admin/component/enable-metadata.component.ts index 42032496b..3c463eb9d 100644 --- a/ui/src/app/admin/component/enable-metadata.component.ts +++ b/ui/src/app/admin/component/enable-metadata.component.ts @@ -7,12 +7,12 @@ import { map } from 'rxjs/operators'; import { MetadataEntity, MetadataResolver } from '../../metadata/domain/model'; import * as fromDashboard from '../../metadata/manager/reducer'; -import * as fromResolver from '../../metadata/resolver/reducer'; +import * as fromMetadata from '../reducer'; import { ToggleEntityDisplay } from '../../metadata/manager/action/manager.action'; import { DeleteDialogComponent } from '../../metadata/manager/component/delete-dialog.component'; import { PreviewEntity } from '../../metadata/domain/action/entity.action'; -import { RemoveDraftRequest } from '../../metadata/resolver/action/draft.action'; -import { LoadAdminResolverRequest } from '../../metadata/resolver/action/collection.action'; +import { FileBackedHttpMetadataResolver } from '../../metadata/domain/entity'; +import { RemoveMetadataRequest, UpdateMetadataRequest, LoadMetadataRequest } from '../action/metadata-collection.action'; @Component({ selector: 'enable-metadata', @@ -20,7 +20,7 @@ import { LoadAdminResolverRequest } from '../../metadata/resolver/action/collect }) export class EnableMetadataComponent implements OnInit { - resolvers$: Observable; + resolvers$: Observable; loading$: Observable; total$: Observable; @@ -34,16 +34,20 @@ export class EnableMetadataComponent implements OnInit { private router: Router, private modalService: NgbModal ) { - this.resolvers$ = store.select(fromResolver.getAllResolvers); - this.loading$ = store.select(fromDashboard.getSearchLoading); - this.entitiesOpen$ = store.select(fromDashboard.getOpenProviders); + this.store.dispatch(new LoadMetadataRequest()); + + this.resolvers$ = this.store + .select(fromMetadata.getMetadataCollection) + .pipe( + map(resolvers => resolvers.map(r => new FileBackedHttpMetadataResolver(r))) + ); + this.loading$ = this.store.select(fromDashboard.getSearchLoading); + this.entitiesOpen$ = this.store.select(fromDashboard.getOpenProviders); this.total$ = this.resolvers$.pipe(map(list => list.length)); } - ngOnInit(): void { - this.store.dispatch(new LoadAdminResolverRequest()); - } + ngOnInit(): void {} edit(entity: MetadataEntity): void { this.router.navigate(['metadata', 'resolver', entity.getId(), 'edit']); @@ -57,13 +61,18 @@ export class EnableMetadataComponent implements OnInit { this.store.dispatch(new PreviewEntity({ id: entity.getId(), entity })); } + toggleResolverEnabled(entity: MetadataResolver, enabled: boolean): void { + let update = { ...entity, serviceEnabled: enabled }; + this.store.dispatch(new UpdateMetadataRequest(update)); + } + deleteResolver(entity: MetadataResolver): void { this.modalService .open(DeleteDialogComponent) .result .then( success => { - this.store.dispatch(new RemoveDraftRequest(entity)); + this.store.dispatch(new RemoveMetadataRequest(entity)); }, err => { console.log('Cancelled'); diff --git a/ui/src/app/admin/component/user-management.component.ts b/ui/src/app/admin/component/user-management.component.ts index cecefa5ae..72a91ff62 100644 --- a/ui/src/app/admin/component/user-management.component.ts +++ b/ui/src/app/admin/component/user-management.component.ts @@ -6,7 +6,7 @@ import * as fromRoot from '../../app.reducer'; import * as fromCore from '../../core/reducer'; import * as fromAdmin from '../reducer'; -import { UpdateAdminRequest, RemoveAdminRequest } from '../action/user-collection.action'; +import { UpdateAdminRequest, RemoveAdminRequest } from '../action/admin-collection.action'; import { Admin } from '../model/admin'; import { DeleteUserDialogComponent } from '../component/delete-user-dialog.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -33,7 +33,7 @@ export class UserManagementComponent implements OnInit { } ngOnInit(): void { - this.users$ = this.store.select(fromAdmin.getAllConfiguredUsers); + this.users$ = this.store.select(fromAdmin.getAllConfiguredAdmins); this.hasUsers$ = this.users$.pipe(map(userList => userList.length > 0)); } diff --git a/ui/src/app/admin/container/action-required.component.ts b/ui/src/app/admin/container/action-required.component.ts index b10d507ef..f504f36e4 100644 --- a/ui/src/app/admin/container/action-required.component.ts +++ b/ui/src/app/admin/container/action-required.component.ts @@ -2,7 +2,6 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; import { Store } from '@ngrx/store'; import * as fromRoot from '../../app.reducer'; -import { LoadNewUsersRequest } from '../action/user-collection.action'; @Component({ selector: 'action-required-page', @@ -14,7 +13,5 @@ export class ActionRequiredPageComponent { constructor( private store: Store - ) { - this.store.dispatch(new LoadNewUsersRequest()); - } + ) {} } diff --git a/ui/src/app/admin/container/admin-management.component.ts b/ui/src/app/admin/container/admin-management.component.ts index 937cd3c98..fa657d5b5 100644 --- a/ui/src/app/admin/container/admin-management.component.ts +++ b/ui/src/app/admin/container/admin-management.component.ts @@ -2,7 +2,7 @@ import { Component, ChangeDetectionStrategy } from '@angular/core'; import { Store } from '@ngrx/store'; import * as fromRoot from '../../app.reducer'; -import { LoadAdminRequest } from '../action/user-collection.action'; +import { LoadAdminRequest } from '../action/admin-collection.action'; @Component({ selector: 'admin-management-page', @@ -14,7 +14,5 @@ export class AdminManagementPageComponent { constructor( private store: Store - ) { - this.store.dispatch(new LoadAdminRequest()); - } + ) {} } diff --git a/ui/src/app/admin/effect/user-collection.effect.ts b/ui/src/app/admin/effect/admin-collection.effect.ts similarity index 88% rename from ui/src/app/admin/effect/user-collection.effect.ts rename to ui/src/app/admin/effect/admin-collection.effect.ts index 2e11979c7..36a71884a 100644 --- a/ui/src/app/admin/effect/user-collection.effect.ts +++ b/ui/src/app/admin/effect/admin-collection.effect.ts @@ -11,9 +11,8 @@ import { UpdateAdminRequest, UpdateAdminSuccess, RemoveAdminRequest, - RemoveAdminSuccess, - LoadNewUsersRequest -} from '../action/user-collection.action'; + RemoveAdminSuccess +} from '../action/admin-collection.action'; import { AdminService } from '../service/admin.service'; import { AddNotification } from '../../notification/action/notification.action'; import { Notification, NotificationType } from '../../notification/model/notification'; @@ -31,14 +30,6 @@ export class AdminCollectionEffects { )) ); - @Effect() - loadNewUsersRequest$ = this.actions$.pipe( - ofType(AdminCollectionActionTypes.LOAD_NEW_USERS_REQUEST), - switchMap(() => this.adminService.queryByRole('ROLE_NONE').pipe( - map(users => new LoadAdminSuccess(users)) - )) - ); - @Effect() updateAdminRequest$ = this.actions$.pipe( ofType(AdminCollectionActionTypes.UPDATE_ADMIN_REQUEST), diff --git a/ui/src/app/admin/effect/metadata-collection.effect.ts b/ui/src/app/admin/effect/metadata-collection.effect.ts new file mode 100644 index 000000000..bb9604916 --- /dev/null +++ b/ui/src/app/admin/effect/metadata-collection.effect.ts @@ -0,0 +1,128 @@ +import { Injectable } from '@angular/core'; +import { Effect, Actions, ofType } from '@ngrx/effects'; +import { Router } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { of } from 'rxjs'; +import { map, catchError, switchMap, tap, withLatestFrom } from 'rxjs/operators'; + +import { + MetadataCollectionActionTypes, + LoadMetadataRequest, + LoadMetadataSuccess, + LoadMetadataError, + RemoveMetadataRequest, + RemoveMetadataSuccess, + RemoveMetadataFail, + UpdateMetadataRequest, + UpdateMetadataSuccess, + UpdateMetadataFail, + UpdateMetadataConflict +} from '../action/metadata-collection.action'; +import { ResolverService } from '../../metadata/domain/service/resolver.service'; +import { removeNulls } from '../../shared/util'; +import { AddNotification } from '../../notification/action/notification.action'; +import { Notification, NotificationType } from '../../notification/model/notification'; +import { I18nService } from '../../i18n/service/i18n.service'; +import * as fromRoot from '../../app.reducer'; +import * as fromI18n from '../../i18n/reducer'; + + +/* istanbul ignore next */ +@Injectable() +export class MetadataCollectionEffects { + + @Effect() + loadMetadatas$ = this.actions$.pipe( + ofType(MetadataCollectionActionTypes.LOAD_METADATA_REQUEST), + switchMap(() => + this.descriptorService + .queryForAdmin() + .pipe( + map(descriptors => new LoadMetadataSuccess(descriptors)), + catchError(error => of(new LoadMetadataError(error))) + ) + ) + ); + + @Effect() + updateMetadata$ = this.actions$.pipe( + ofType(MetadataCollectionActionTypes.UPDATE_METADATA_REQUEST), + map(action => action.payload), + switchMap(provider => { + return this.descriptorService + .update(removeNulls(provider)) + .pipe( + map(p => new UpdateMetadataSuccess({ + id: p.id, + changes: p + })), + catchError(err => { + if (err.status === 409) { + return of(new UpdateMetadataConflict(provider)); + } + return of(new UpdateMetadataFail({ + errorCode: err.status, + errorMessage: `${err.statusText} - ${err.message}` + })); + }) + ); + }) + ); + + @Effect() + removeMetadataSuccessReload$ = this.actions$.pipe( + ofType(MetadataCollectionActionTypes.REMOVE_METADATA_SUCCESS), + map(action => action.payload), + map(provider => new LoadMetadataRequest()) + ); + + @Effect() + updateMetadataSuccessNotification$ = this.actions$.pipe( + ofType(MetadataCollectionActionTypes.UPDATE_METADATA_SUCCESS), + map(action => action.payload), + withLatestFrom(this.store.select(fromI18n.getMessages)), + map(([error, messages]) => new AddNotification( + new Notification( + NotificationType.Success, + `Metadata Source has been enabled`, + 8000 + ) + )) + ); + + @Effect() + updateMetadataFailNotification$ = this.actions$.pipe( + ofType(MetadataCollectionActionTypes.UPDATE_METADATA_FAIL), + map(action => action.payload), + withLatestFrom(this.store.select(fromI18n.getMessages)), + map(([error, messages]) => new AddNotification( + new Notification( + NotificationType.Danger, + `${error.errorCode}: ${this.i18nService.translate(error.errorMessage, null, messages)}`, + 8000 + ) + )) + ); + + @Effect() + removeMetadata$ = this.actions$.pipe( + ofType(MetadataCollectionActionTypes.REMOVE_METADATA), + map(action => action.payload), + switchMap(entity => + this.descriptorService + .remove(entity) + .pipe( + map(p => new RemoveMetadataSuccess(entity)), + catchError(err => of(new RemoveMetadataFail(err))) + ) + ) + ); + + constructor( + private descriptorService: ResolverService, + private actions$: Actions, + private router: Router, + private store: Store, + private i18nService: I18nService + ) { } +} /* istanbul ignore next */ diff --git a/ui/src/app/admin/reducer/user-collection.reducer.spec.ts b/ui/src/app/admin/reducer/admin-collection.reducer.spec.ts similarity index 93% rename from ui/src/app/admin/reducer/user-collection.reducer.spec.ts rename to ui/src/app/admin/reducer/admin-collection.reducer.spec.ts index 34b75defd..4c26df392 100644 --- a/ui/src/app/admin/reducer/user-collection.reducer.spec.ts +++ b/ui/src/app/admin/reducer/admin-collection.reducer.spec.ts @@ -1,11 +1,11 @@ -import { reducer, initialState as snapshot } from './user-collection.reducer'; -import * as fromAdmin from './user-collection.reducer'; +import { reducer, initialState as snapshot } from './admin-collection.reducer'; +import * as fromAdmin from './admin-collection.reducer'; import { AdminCollectionActionTypes, LoadAdminSuccess, UpdateAdminSuccess, RemoveAdminSuccess -} from '../action/user-collection.action'; +} from '../action/admin-collection.action'; import { Admin } from '../model/admin'; let users = [ diff --git a/ui/src/app/admin/reducer/user-collection.reducer.ts b/ui/src/app/admin/reducer/admin-collection.reducer.ts similarity index 97% rename from ui/src/app/admin/reducer/user-collection.reducer.ts rename to ui/src/app/admin/reducer/admin-collection.reducer.ts index e11be45ef..ce404d57d 100644 --- a/ui/src/app/admin/reducer/user-collection.reducer.ts +++ b/ui/src/app/admin/reducer/admin-collection.reducer.ts @@ -1,6 +1,6 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Admin } from '../model/admin'; -import { AdminCollectionActionsUnion, AdminCollectionActionTypes } from '../action/user-collection.action'; +import { AdminCollectionActionsUnion, AdminCollectionActionTypes } from '../action/admin-collection.action'; export interface CollectionState extends EntityState { selectedAdminId: string | null; diff --git a/ui/src/app/admin/reducer/index.spec.ts b/ui/src/app/admin/reducer/index.spec.ts new file mode 100644 index 000000000..f93b0557e --- /dev/null +++ b/ui/src/app/admin/reducer/index.spec.ts @@ -0,0 +1,40 @@ +import * as fromIndex from './'; +import { MetadataResolver } from '../../metadata/domain/model'; +import { User } from '../../core/model/user'; + +// export const totalUserFn = (users) => users.length; +// export const totalMetadataFn = (md) => md.filter(obj => !obj.serviceEnabled).length; +// export const totalActionsFn = (users, md) => md + users; + +let resolvers: MetadataResolver[] = [ + { id: '1', entityId: 'foo', serviceEnabled: true, serviceProviderName: 'bar', createdDate: 'Date' } as MetadataResolver, + { id: '2', entityId: 'baz', serviceEnabled: false, serviceProviderName: 'fin', createdDate: 'Date' } as MetadataResolver +]; + +let users: User[] = [ + { + username: 'foo', + role: 'admin', + firstName: 'foo', + lastName: 'bar', + emailAddress: 'foo@bar.com' + } +]; + +describe('admin dashboard state selectors', () => { + describe('totalUserFn', () => { + it('should get the length of the provided array', () => { + expect(fromIndex.totalUserFn(users)).toBe(1); + }); + }); + describe('totalMetadataFn', () => { + it('should get the length of the provided list after filtering enabled resolvers', () => { + expect(fromIndex.totalMetadataFn(resolvers)).toBe(1); + }); + }); + describe('totalActionsFn', () => { + it('should return the sum of the total users and metadata', () => { + expect(fromIndex.totalActionsFn(1, 2)).toBe(3); + }); + }); +}); diff --git a/ui/src/app/admin/reducer/index.ts b/ui/src/app/admin/reducer/index.ts index f647e200e..651870b76 100644 --- a/ui/src/app/admin/reducer/index.ts +++ b/ui/src/app/admin/reducer/index.ts @@ -1,37 +1,61 @@ import { createSelector, createFeatureSelector } from '@ngrx/store'; import * as fromRoot from '../../core/reducer'; -import * as fromCollection from './user-collection.reducer'; +import * as fromAdminCollection from './admin-collection.reducer'; +import * as fromMetadataCollection from './metadata-collection.reducer'; +import { getInCollectionFn } from '../../metadata/domain/domain.util'; export interface AdminState { - collection: fromCollection.CollectionState; + admins: fromAdminCollection.CollectionState; + metadata: fromMetadataCollection.CollectionState; } export const reducers = { - collection: fromCollection.reducer + admins: fromAdminCollection.reducer, + metadata: fromMetadataCollection.reducer }; export interface State extends fromRoot.State { 'admin': AdminState; } -export const getCollectionFromStateFn = (state: AdminState) => state.collection; +export const getAdminsCollectionFromStateFn = (state: AdminState) => state.admins; +export const getMetadataCollectionFromStateFn = (state: AdminState) => state.metadata; -export const getAdminState = createFeatureSelector('admin'); +export const getFeatureState = createFeatureSelector('admin'); /* * Select pieces of Admin Collection */ -export const getCollectionState = createSelector(getAdminState, getCollectionFromStateFn); -export const getAllAdmins = createSelector(getCollectionState, fromCollection.selectAllAdmins); -export const getCollectionSaving = createSelector(getCollectionState, fromCollection.getIsSaving); +export const getAdminCollectionState = createSelector(getFeatureState, getAdminsCollectionFromStateFn); +export const getAllAdmins = createSelector(getAdminCollectionState, fromAdminCollection.selectAllAdmins); +export const getCollectionSaving = createSelector(getAdminCollectionState, fromAdminCollection.getIsSaving); -export const getAdminEntities = createSelector(getCollectionState, fromCollection.selectAdminEntities); -export const getSelectedAdminId = createSelector(getCollectionState, fromCollection.getSelectedAdminId); +export const getAdminEntities = createSelector(getAdminCollectionState, fromAdminCollection.selectAdminEntities); +export const getSelectedAdminId = createSelector(getAdminCollectionState, fromAdminCollection.getSelectedAdminId); export const getSelectedAdmin = createSelector(getAdminEntities, getSelectedAdminId, (entities, selectedId) => { return selectedId && entities[selectedId]; }); -export const getAdminIds = createSelector(getCollectionState, fromCollection.selectAdminIds); +export const getAdminIds = createSelector(getAdminCollectionState, fromAdminCollection.selectAdminIds); +export const getAllConfiguredAdmins = createSelector(getAllAdmins, (admins) => admins.filter(a => a.role !== 'ROLE_NONE')); export const getAllNewUsers = createSelector(getAllAdmins, (admins) => admins.filter(a => a.role === 'ROLE_NONE')); -export const getAllConfiguredUsers = createSelector(getAllAdmins, (admins) => admins.filter(a => a.role !== 'ROLE_NONE')); -export const getTotalActionsRequired = createSelector(getAllNewUsers, (users) => users.length); +/* + * Select pieces of Metadata Collection +*/ +export const getMetadataCollectionState = createSelector(getFeatureState, getMetadataCollectionFromStateFn); + +export const getMetadataEntities = createSelector(getMetadataCollectionState, fromMetadataCollection.selectMetadataEntities); +export const getSelectedMetadataId = createSelector(getMetadataCollectionState, fromMetadataCollection.getSelectedMetadataId); +export const getMetadataIds = createSelector(getMetadataCollectionState, fromMetadataCollection.selectMetadataIds); + +export const getMetadataCollection = createSelector(getMetadataCollectionState, getMetadataIds, fromMetadataCollection.selectAllMetadata); +export const getSelectedMetadata = createSelector(getMetadataEntities, getSelectedMetadataId, getInCollectionFn); + +export const totalUserFn = (users) => users.length; +export const totalMetadataFn = (md) => md.filter(obj => !obj.serviceEnabled).length; +export const totalActionsFn = (users, md) => md + users; + +export const getTotalNewUsers = createSelector(getAllNewUsers, totalUserFn); +export const getTotalNewMetadata = createSelector(getMetadataCollection, totalMetadataFn); + +export const getTotalActionsRequired = createSelector(getTotalNewUsers, getTotalNewMetadata, totalActionsFn); diff --git a/ui/src/app/admin/reducer/metadata-collection.reducer.spec.ts b/ui/src/app/admin/reducer/metadata-collection.reducer.spec.ts new file mode 100644 index 000000000..a1b5b0c70 --- /dev/null +++ b/ui/src/app/admin/reducer/metadata-collection.reducer.spec.ts @@ -0,0 +1,72 @@ +import { reducer } from './metadata-collection.reducer'; +import * as fromCollection from './metadata-collection.reducer'; +import * as resolverActions from '../action/metadata-collection.action'; +import { MetadataResolver } from '../../metadata/domain/model'; + +let resolvers: MetadataResolver[] = [ + { id: '1', entityId: 'foo', serviceProviderName: 'bar', createdDate: 'Tue Apr 17 2018 13:33:54 GMT-0700 (MST)' } as MetadataResolver, + { id: '2', entityId: 'baz', serviceProviderName: 'fin', createdDate: 'Tue Apr 17 2018 13:34:07 GMT-0700 (MST)' } as MetadataResolver +], +snapshot: fromCollection.CollectionState = { + ids: [resolvers[0].id, resolvers[1].id], + entities: { + [resolvers[0].id]: resolvers[0], + [resolvers[1].id]: resolvers[1] + }, + selectedMetadataId: null +}; + +describe('Resolver Reducer', () => { + const initialState: fromCollection.CollectionState = { + ids: [], + entities: {}, + selectedMetadataId: null, + }; + + describe('undefined action', () => { + it('should return the default state', () => { + const result = reducer(undefined, {} as any); + + expect(result).toEqual(initialState); + }); + }); + + describe('Load Providers: Success', () => { + it('should add the loaded resolvers to the collection', () => { + const action = new resolverActions.LoadMetadataSuccess(resolvers); + const result = reducer(initialState, action); + + expect(result).toEqual( + Object.assign({}, initialState, snapshot) + ); + }); + }); + + describe('Update Providers: Success', () => { + it('should update the draft of the specified id', () => { + let changes = { ...resolvers[1], serviceEnabled: true }, + expected = { + ids: [resolvers[0].id, resolvers[1].id], + entities: { + [resolvers[0].id]: resolvers[0], + [resolvers[1].id]: changes + }, + selectedMetadataId: null + }; + const action = new resolverActions.UpdateMetadataSuccess({ id: changes.id, changes }); + const result = reducer({ ...snapshot }, action); + + expect(result).toEqual( + Object.assign({}, initialState, expected) + ); + }); + + it('should return state if the entityId is not found', () => { + let changes = { ...resolvers[1], serviceEnabled: true, id: '4' }; + const action = new resolverActions.UpdateMetadataSuccess({ id: changes.id, changes }); + const result = reducer({ ...snapshot }, action); + + expect(result).toEqual(snapshot); + }); + }); +}); diff --git a/ui/src/app/admin/reducer/metadata-collection.reducer.ts b/ui/src/app/admin/reducer/metadata-collection.reducer.ts new file mode 100644 index 000000000..dc4006841 --- /dev/null +++ b/ui/src/app/admin/reducer/metadata-collection.reducer.ts @@ -0,0 +1,59 @@ +import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; +import { MetadataResolver } from '../../metadata/domain/model'; +import { MetadataCollectionActionsUnion, MetadataCollectionActionTypes } from '../action/metadata-collection.action'; + +export interface CollectionState extends EntityState { + selectedMetadataId: string | null; +} + +export function sortByDate(a: MetadataResolver, b: MetadataResolver): number { + return a.createdDate.localeCompare(b.createdDate); +} + +export const adapter: EntityAdapter = createEntityAdapter({ + sortComparer: sortByDate, + selectId: (model: MetadataResolver) => model.id +}); + +export const initialState: CollectionState = adapter.getInitialState({ + selectedMetadataId: null +}); + +export function reducer(state = initialState, action: MetadataCollectionActionsUnion): CollectionState { + switch (action.type) { + case MetadataCollectionActionTypes.LOAD_METADATA_SUCCESS: { + return adapter.addAll(action.payload, { + ...state, + selectedMetadataId: state.selectedMetadataId + }); + } + + case MetadataCollectionActionTypes.UPDATE_METADATA_SUCCESS: { + return adapter.updateOne(action.payload, state); + } + + case MetadataCollectionActionTypes.LOAD_METADATA_ERROR: { + return adapter.removeAll({ + ...state + }); + } + + case MetadataCollectionActionTypes.REMOVE_METADATA_SUCCESS: { + return adapter.removeOne(action.payload.id, { + ...state + }); + } + + default: { + return state; + } + } +} + +export const getSelectedMetadataId = (state: CollectionState) => state.selectedMetadataId; +export const { + selectIds: selectMetadataIds, + selectEntities: selectMetadataEntities, + selectAll: selectAllMetadata, + selectTotal: selectMetadataTotal +} = adapter.getSelectors(); diff --git a/ui/src/app/core/model/user.ts b/ui/src/app/core/model/user.ts index 37d05212e..27375c23e 100644 --- a/ui/src/app/core/model/user.ts +++ b/ui/src/app/core/model/user.ts @@ -1,8 +1,7 @@ export interface User { - id: string; + username: string; role: string; - name: { - first: string, - last: string - }; + firstName: string; + lastName: string; + emailAddress: string; } diff --git a/ui/src/app/core/reducer/user.reducer.spec.ts b/ui/src/app/core/reducer/user.reducer.spec.ts index 6dc2bed01..587acca0f 100644 --- a/ui/src/app/core/reducer/user.reducer.spec.ts +++ b/ui/src/app/core/reducer/user.reducer.spec.ts @@ -11,12 +11,11 @@ describe('User Reducer', () => { }; const user: User = { - id: '1', + username: 'foo', role: 'admin', - name: { - first: 'foo', - last: 'bar' - } + firstName: 'foo', + lastName: 'bar', + emailAddress: 'foo@bar.com' }; describe('undefined action', () => { @@ -59,14 +58,7 @@ describe('User Reducer', () => { describe('User Selectors', () => { const state = { - user: { - id: '1', - role: 'admin', - name: { - first: 'foo', - last: 'bar' - } - }, + user: { ...user }, fetching: true, error: { message: 'foo', type: 'bar' } } as fromUser.UserState; diff --git a/ui/src/app/dashboard/container/dashboard.component.ts b/ui/src/app/dashboard/container/dashboard.component.ts index 4c3aab787..543252e94 100644 --- a/ui/src/app/dashboard/container/dashboard.component.ts +++ b/ui/src/app/dashboard/container/dashboard.component.ts @@ -4,7 +4,6 @@ import { Store } from '@ngrx/store'; import * as fromRoot from '../../app.reducer'; import * as fromAdmin from '../../admin/reducer'; import { Observable } from 'rxjs'; -import { LoadAdminRequest } from '../../admin/action/user-collection.action'; import { LoadRoleRequest } from '../../core/action/configuration.action'; import { map } from 'rxjs/operators'; diff --git a/ui/src/app/metadata/domain/service/resolver.service.ts b/ui/src/app/metadata/domain/service/resolver.service.ts index 3c3e48602..2a158f75f 100644 --- a/ui/src/app/metadata/domain/service/resolver.service.ts +++ b/ui/src/app/metadata/domain/service/resolver.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; -import { Observable, throwError } from 'rxjs'; +import { Observable, throwError, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { MetadataResolver } from '../model'; @@ -14,13 +14,20 @@ export class ResolverService { private http: HttpClient ) {} - query(opts: any = {}): Observable { + query(): Observable { return this.http.get(`${ this.base }${ this.endpoint }s`, {}) .pipe( catchError(err => throwError([])) ); } + queryForAdmin(): Observable { + return this.http.get(`${this.base}${this.endpoint}/disabledNonAdmin`, {}) + .pipe( + catchError(err => throwError([])) + ); + } + find(id: string): Observable { return this.http.get(`${ this.base }${ this.endpoint }/${ id }`) .pipe( diff --git a/ui/src/app/metadata/manager/component/entity-item.component.ts b/ui/src/app/metadata/manager/component/entity-item.component.ts index b2453c474..f9d1745c3 100644 --- a/ui/src/app/metadata/manager/component/entity-item.component.ts +++ b/ui/src/app/metadata/manager/component/entity-item.component.ts @@ -12,6 +12,9 @@ import { MetadataTypes } from '../../domain/domain.type'; export class EntityItemComponent { types = MetadataTypes; @Input() isOpen: boolean; + @Input() allowDelete: boolean; + @Input() showAdminFunctions: boolean; + @Output() toggleEnabled = new EventEmitter(); @Output() select = new EventEmitter(); @Output() toggle = new EventEmitter(); @Output() preview = new EventEmitter(); diff --git a/ui/src/app/metadata/manager/component/resolver-item.component.html b/ui/src/app/metadata/manager/component/resolver-item.component.html index ff734e4a6..0524c9d10 100644 --- a/ui/src/app/metadata/manager/component/resolver-item.component.html +++ b/ui/src/app/metadata/manager/component/resolver-item.component.html @@ -1,7 +1,7 @@
-
+
  @@ -21,11 +21,27 @@ {{ entity.getDisplayId() }}
-
- + -
@@ -66,7 +82,7 @@
-