From 41e4d7a748ed3ae5a7be69e7623fc0ead1b3115a Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 16 Mar 2021 10:12:22 -0700 Subject: [PATCH 1/4] Added navigation to edit filter --- ui/src/app/app.routing.ts | 3 +- .../container/metadata-options.component.ts | 10 +- .../domain/component/editor-nav.component.ts | 2 +- .../filter/action/collection.action.ts | 32 +++-- .../metadata/filter/action/editor.action.ts | 28 +++++ .../metadata/filter/action/filter.action.ts | 13 +- .../container/edit-filter-step.component.html | 16 +++ .../container/edit-filter-step.component.ts | 119 ++++++++++++++++++ .../container/edit-filter.component.html | 25 ++-- .../filter/container/edit-filter.component.ts | 36 +++--- .../filter/container/filter.component.ts | 21 ++-- .../filter/container/new-filter.component.ts | 6 +- .../container/select-filter.component.ts | 20 ++- .../filter/effect/collection.effect.ts | 45 ++++--- .../metadata/filter/effect/editor.effect.ts | 52 ++++++++ .../metadata/filter/effect/filter.effect.ts | 21 ++-- ui/src/app/metadata/filter/model/index.ts | 2 +- .../filter/reducer/collection.reducer.ts | 2 +- .../metadata/filter/reducer/editor.reducer.ts | 47 +++++++ ui/src/app/metadata/filter/reducer/index.ts | 15 +++ .../container/provider-edit.component.ts | 4 +- .../provider-filter-list.component.ts | 14 ++- .../container/provider-select.component.ts | 3 +- .../app/metadata/provider/provider.routing.ts | 52 ++++---- 24 files changed, 478 insertions(+), 110 deletions(-) create mode 100644 ui/src/app/metadata/filter/action/editor.action.ts create mode 100644 ui/src/app/metadata/filter/container/edit-filter-step.component.html create mode 100644 ui/src/app/metadata/filter/container/edit-filter-step.component.ts create mode 100644 ui/src/app/metadata/filter/effect/editor.effect.ts create mode 100644 ui/src/app/metadata/filter/reducer/editor.reducer.ts diff --git a/ui/src/app/app.routing.ts b/ui/src/app/app.routing.ts index 92a7ae4cd..bceb76c2c 100644 --- a/ui/src/app/app.routing.ts +++ b/ui/src/app/app.routing.ts @@ -19,7 +19,8 @@ const routes: Routes = [ scrollOffset: [0, 64], anchorScrolling: 'enabled', scrollPositionRestoration: 'enabled', - relativeLinkResolution: 'legacy' + relativeLinkResolution: 'legacy', + paramsInheritanceStrategy: 'always' })], exports: [RouterModule] }) diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.ts b/ui/src/app/metadata/configuration/container/metadata-options.component.ts index 411c6fb10..19b3a12e4 100644 --- a/ui/src/app/metadata/configuration/container/metadata-options.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.ts @@ -93,11 +93,17 @@ export class MetadataOptionsComponent implements OnDestroy { } updateOrderUp(filter: MetadataFilter): void { - this.store.dispatch(new ChangeFilterOrderUp(filter.resourceId)); + this.store.dispatch(new ChangeFilterOrderUp({ + id: filter.resourceId, + providerId: this.activatedRoute.snapshot.params.providerId + })); } updateOrderDown(filter: MetadataFilter): void { - this.store.dispatch(new ChangeFilterOrderDown(filter.resourceId)); + this.store.dispatch(new ChangeFilterOrderDown({ + id: filter.resourceId, + providerId: this.activatedRoute.snapshot.params.providerId + })); } removeFilter(id: string): void { diff --git a/ui/src/app/metadata/domain/component/editor-nav.component.ts b/ui/src/app/metadata/domain/component/editor-nav.component.ts index 0aa47c088..32653fb43 100644 --- a/ui/src/app/metadata/domain/component/editor-nav.component.ts +++ b/ui/src/app/metadata/domain/component/editor-nav.component.ts @@ -2,7 +2,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -import { skipWhile, combineLatest, map } from 'rxjs/operators'; +import { skipWhile, map } from 'rxjs/operators'; import { WizardStep } from '../../../wizard/model'; import * as fromWizard from '../../../wizard/reducer'; diff --git a/ui/src/app/metadata/filter/action/collection.action.ts b/ui/src/app/metadata/filter/action/collection.action.ts index 705f0f65d..11ffad3ca 100644 --- a/ui/src/app/metadata/filter/action/collection.action.ts +++ b/ui/src/app/metadata/filter/action/collection.action.ts @@ -79,13 +79,19 @@ export class LoadFilterError implements Action { export class UpdateFilterRequest implements Action { readonly type = FilterCollectionActionTypes.UPDATE_FILTER_REQUEST; - constructor(public payload: MetadataFilter) { } + constructor(public payload: { + filter: MetadataFilter, + providerId: string; + }) { } } export class UpdateFilterSuccess implements Action { readonly type = FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS; - constructor(public payload: Update) { } + constructor(public payload: { + providerId: string, + update: Update + }) { } } export class UpdateFilterFail implements Action { @@ -97,7 +103,10 @@ export class UpdateFilterFail implements Action { export class UpdateFilterConflict implements Action { readonly type = FilterCollectionActionTypes.UPDATE_FILTER_CONFLICT; - constructor(public payload: MetadataFilter) { } + constructor(public payload: { + providerId: string, + filter: MetadataFilter + }) { } } export class AddFilterRequest implements Action { @@ -143,13 +152,16 @@ export class ClearFilters implements Action { export class SetOrderFilterRequest implements Action { readonly type = FilterCollectionActionTypes.SET_ORDER_FILTER_REQUEST; - constructor(public payload: string[]) { } + constructor(public payload: { + order: string[], + providerId: string + }) { } } export class SetOrderFilterSuccess implements Action { readonly type = FilterCollectionActionTypes.SET_ORDER_FILTER_SUCCESS; - constructor() { } + constructor(public payload: string) { } } export class SetOrderFilterFail implements Action { @@ -179,13 +191,19 @@ export class GetOrderFilterFail implements Action { export class ChangeFilterOrderUp implements Action { readonly type = FilterCollectionActionTypes.CHANGE_FILTER_ORDER_UP; - constructor(public payload: string) { } + constructor(public payload: { + id: string, + providerId: string + }) { } } export class ChangeFilterOrderDown implements Action { readonly type = FilterCollectionActionTypes.CHANGE_FILTER_ORDER_DOWN; - constructor(public payload: string) { } + constructor(public payload: { + id: string, + providerId: string + }) { } } export type FilterCollectionActionsUnion = diff --git a/ui/src/app/metadata/filter/action/editor.action.ts b/ui/src/app/metadata/filter/action/editor.action.ts new file mode 100644 index 000000000..7c4dba2cf --- /dev/null +++ b/ui/src/app/metadata/filter/action/editor.action.ts @@ -0,0 +1,28 @@ +import { Action } from '@ngrx/store'; + +export enum EditorActionTypes { + UPDATE_STATUS = '[Filter Editor] Update Status', + SELECT_PROVIDER_TYPE = '[Filter Editor] Select Filter Type', + CLEAR = '[Filter Editor] Clear' +} + +export class UpdateStatus implements Action { + readonly type = EditorActionTypes.UPDATE_STATUS; + + constructor(public payload: { [key: string]: string }) { } +} + +export class SelectFilterType implements Action { + readonly type = EditorActionTypes.SELECT_PROVIDER_TYPE; + + constructor(public payload: string) { } +} + +export class ClearEditor implements Action { + readonly type = EditorActionTypes.CLEAR; +} + +export type EditorActionUnion = + | UpdateStatus + | SelectFilterType + | ClearEditor; diff --git a/ui/src/app/metadata/filter/action/filter.action.ts b/ui/src/app/metadata/filter/action/filter.action.ts index a745d78c4..b13e2ee8f 100644 --- a/ui/src/app/metadata/filter/action/filter.action.ts +++ b/ui/src/app/metadata/filter/action/filter.action.ts @@ -10,7 +10,9 @@ export enum FilterActionTypes { CLEAR_FILTER = '[Filter] Clear Filter', LOAD_ENTITY_PREVIEW = '[Filter] Load Preview data', LOAD_ENTITY_PREVIEW_SUCCESS = '[Filter] Load Preview data success', - LOAD_ENTITY_PREVIEW_ERROR = '[Filter] Load Preview data error' + LOAD_ENTITY_PREVIEW_ERROR = '[Filter] Load Preview data error', + + RESET_CHANGES = '[Filter] Reset Changes' } export class SelectId implements Action { @@ -37,6 +39,8 @@ export class LoadEntityPreviewError implements Action { export class CancelCreateFilter implements Action { readonly type = FilterActionTypes.CANCEL_CREATE_FILTER; + + constructor(public payload: string) { } } export class ClearFilter implements Action { @@ -55,6 +59,10 @@ export class SelectFilterType implements Action { constructor(public payload: string) { } } +export class ResetChanges implements Action { + readonly type = FilterActionTypes.RESET_CHANGES; +} + export type FilterActionsUnion = | SelectId | SelectFilterType @@ -63,4 +71,5 @@ export type FilterActionsUnion = | LoadEntityPreview | LoadEntityPreviewSuccess | LoadEntityPreviewError - | ClearFilter; + | ClearFilter + | ResetChanges; diff --git a/ui/src/app/metadata/filter/container/edit-filter-step.component.html b/ui/src/app/metadata/filter/container/edit-filter-step.component.html new file mode 100644 index 000000000..ad969fd9e --- /dev/null +++ b/ui/src/app/metadata/filter/container/edit-filter-step.component.html @@ -0,0 +1,16 @@ + +
+ + + {{ lock.value ? 'Locked' : 'Unlocked' }} + + For Advanced Knowledge Only +
+ +
\ No newline at end of file diff --git a/ui/src/app/metadata/filter/container/edit-filter-step.component.ts b/ui/src/app/metadata/filter/container/edit-filter-step.component.ts new file mode 100644 index 000000000..281b86b95 --- /dev/null +++ b/ui/src/app/metadata/filter/container/edit-filter-step.component.ts @@ -0,0 +1,119 @@ +import { Component, OnDestroy } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { Subject, Observable, Subscription } from 'rxjs'; + +import * as fromFilter from '../reducer'; +import { MetadataFilterTypes } from '../model'; +import { FormDefinition } from '../../../wizard/model'; +import { MetadataFilter } from '../../domain/model'; +import { SchemaService } from '../../../schema-form/service/schema.service'; +import { UpdateFilterRequest } from '../action/collection.action'; +import { CancelCreateFilter, UpdateFilterChanges } from '../action/filter.action'; +import { PreviewEntity } from '../../domain/action/entity.action'; +import { shareReplay, map, withLatestFrom, filter, switchMap, startWith, defaultIfEmpty, takeUntil } from 'rxjs/operators'; + +@Component({ + selector: 'edit-filter-step-page', + templateUrl: './edit-filter-step.component.html' +}) +export class EditFilterStepComponent implements OnDestroy { + + private ngUnsubscribe: Subject = new Subject(); + + valueChangeSubject = new Subject>(); + private valueChangeEmitted$ = this.valueChangeSubject.asObservable(); + + statusChangeSubject = new Subject<{ value: any[] }>(); + private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); + + definition$: Observable>; + definition: FormDefinition; + schema$: Observable; + + model$: Observable; + isSaving$: Observable; + filter: MetadataFilter; + isValid: boolean; + type$: Observable; + + validators$: Observable<{ [key: string]: any }>; + + actions: any; + + defSub: Subscription; + + constructor( + private store: Store, + private schemaService: SchemaService + ) { + this.definition$ = this.store.select(fromFilter.getFilterType).pipe( + takeUntil(this.ngUnsubscribe), + filter(t => !!t), + map(t => MetadataFilterTypes[t]) + ); + + this.defSub = this.definition$.subscribe(d => this.definition = d); + + this.definition$.subscribe(console.log); + + this.schema$ = this.definition$.pipe( + takeUntil(this.ngUnsubscribe), + filter(d => !!d), + switchMap(d => { + return this.schemaService.get(d.schema).pipe(takeUntil(this.ngUnsubscribe)); + }), + shareReplay() + ); + this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); + this.model$ = this.store.select(fromFilter.getSelectedFilter); + this.type$ = this.model$.pipe(map(f => f && f.hasOwnProperty('@type') ? f['@type'] : '')); + + this.valueChangeEmitted$.subscribe(changes => this.store.dispatch(new UpdateFilterChanges(changes.value))); + this.statusChangeEmitted$.subscribe(valid => { + this.isValid = valid.value ? valid.value.length === 0 : true; + }); + + this.validators$ = this.store.select(fromFilter.getFilterNames).pipe( + takeUntil(this.ngUnsubscribe), + withLatestFrom( + this.store.select(fromFilter.getSelectedFilter), + this.definition$ + ), + map(([names, filter, definition]) => definition.getValidators( + names.filter(n => n !== filter.name) + )) + ); + + this.store + .select(fromFilter.getFilter) + .subscribe(filter => this.filter = filter); + + this.actions = { + preview: (property: any, parameters: any) => { + this.preview(parameters.filterId); + } + }; + } + + ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); + this.defSub.unsubscribe(); + } + + save(): void { + this.store.dispatch(new UpdateFilterRequest(this.filter)); + } + + cancel(): void { + this.store.dispatch(new CancelCreateFilter()); + } + + preview(id: string): void { + this.store.dispatch(new PreviewEntity({ + id, + entity: this.definition.getEntity(this.filter) + })); + } +} + diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.html b/ui/src/app/metadata/filter/container/edit-filter.component.html index 2d36ceb75..cf62ba24b 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.html +++ b/ui/src/app/metadata/filter/container/edit-filter.component.html @@ -38,13 +38,24 @@ - +
+
+
+ + +
+
+ +
+
diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.ts b/ui/src/app/metadata/filter/container/edit-filter.component.ts index de25389a8..d70537a3f 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -10,8 +10,10 @@ import { SchemaService } from '../../../schema-form/service/schema.service'; import { UpdateFilterRequest } from '../action/collection.action'; import { CancelCreateFilter, UpdateFilterChanges } from '../action/filter.action'; import { PreviewEntity } from '../../domain/action/entity.action'; -import { EntityAttributesFilterEntity } from '../../domain/entity'; -import { shareReplay, map, withLatestFrom, filter, switchMap, startWith, defaultIfEmpty, takeUntil } from 'rxjs/operators'; +import { NAV_FORMATS } from '../../domain/component/editor-nav.component'; +import { shareReplay, map, withLatestFrom, filter, switchMap, takeUntil } from 'rxjs/operators'; +import * as fromWizard from '../../../wizard/reducer'; +import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'edit-filter-page', @@ -37,32 +39,33 @@ export class EditFilterComponent implements OnDestroy { isValid: boolean; type$: Observable; + status$: Observable; + validators$: Observable<{ [key: string]: any }>; actions: any; defSub: Subscription; + formats = NAV_FORMATS; + constructor( private store: Store, - private schemaService: SchemaService + private schemaService: SchemaService, + private route: ActivatedRoute ) { - this.definition$ = this.store.select(fromFilter.getFilterType).pipe( - takeUntil(this.ngUnsubscribe), - filter(t => !!t), - map(t => MetadataFilterTypes[t]) - ); + + this.definition$ = this.store.select(fromWizard.getWizardDefinition).pipe(filter(d => !!d)) this.defSub = this.definition$.subscribe(d => this.definition = d); this.schema$ = this.definition$.pipe( takeUntil(this.ngUnsubscribe), filter(d => !!d), - switchMap(d => { - return this.schemaService.get(d.schema).pipe(takeUntil(this.ngUnsubscribe)); - }), + switchMap(d => this.schemaService.get(d.schema).pipe(takeUntil(this.ngUnsubscribe))), shareReplay() ); + this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); this.model$ = this.store.select(fromFilter.getSelectedFilter); this.type$ = this.model$.pipe(map(f => f && f.hasOwnProperty('@type') ? f['@type'] : '')); @@ -72,6 +75,8 @@ export class EditFilterComponent implements OnDestroy { this.isValid = valid.value ? valid.value.length === 0 : true; }); + this.status$ = this.store.select(fromFilter.getInvalidEditorForms); + this.validators$ = this.store.select(fromFilter.getFilterNames).pipe( takeUntil(this.ngUnsubscribe), withLatestFrom( @@ -89,7 +94,7 @@ export class EditFilterComponent implements OnDestroy { this.actions = { preview: (property: any, parameters: any) => { - this.preview(parameters.id); + this.preview(parameters.filterId); } }; } @@ -101,11 +106,14 @@ export class EditFilterComponent implements OnDestroy { } save(): void { - this.store.dispatch(new UpdateFilterRequest(this.filter)); + this.store.dispatch(new UpdateFilterRequest({ + filter: this.filter, + providerId: this.route.snapshot.params.providerId + })); } cancel(): void { - this.store.dispatch(new CancelCreateFilter()); + this.store.dispatch(new CancelCreateFilter(this.route.snapshot.params.providerId)); } preview(id: string): void { diff --git a/ui/src/app/metadata/filter/container/filter.component.ts b/ui/src/app/metadata/filter/container/filter.component.ts index 6dc02e037..bd6054990 100644 --- a/ui/src/app/metadata/filter/container/filter.component.ts +++ b/ui/src/app/metadata/filter/container/filter.component.ts @@ -1,12 +1,15 @@ import { Component, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable, Subscription } from 'rxjs'; -import { distinctUntilChanged, map } from 'rxjs/operators'; +import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { MetadataFilter } from '../../domain/model/metadata-filter'; import { LoadFilterRequest } from '../action/collection.action'; import * as fromFilter from '../reducer'; +import * as fromProviders from '../../provider/reducer'; +import { SelectProviderRequest } from '../../provider/action/collection.action'; +import { MetadataProvider } from '../../domain/model'; @Component({ selector: 'filter-page', @@ -15,22 +18,22 @@ import * as fromFilter from '../reducer'; providers: [] }) export class FilterComponent implements OnDestroy { - actionsSubscription: Subscription; + filterSelectSubscription: Subscription; filters$: Observable; + provider$: Observable; constructor( private store: Store, private route: ActivatedRoute ) { - this.actionsSubscription = this.route.parent.params.pipe( - distinctUntilChanged(), - map(params => { - return new LoadFilterRequest(params.providerId); - }) - ).subscribe(store); + const params$ = this.route.params.pipe(distinctUntilChanged()); + + this.filterSelectSubscription = params$.pipe( + map(params => new LoadFilterRequest(params.providerId)) + ).subscribe(this.store); } ngOnDestroy() { - this.actionsSubscription.unsubscribe(); + this.filterSelectSubscription.unsubscribe(); } } /* istanbul ignore next */ diff --git a/ui/src/app/metadata/filter/container/new-filter.component.ts b/ui/src/app/metadata/filter/container/new-filter.component.ts index 4afeac3d9..785473738 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.ts +++ b/ui/src/app/metadata/filter/container/new-filter.component.ts @@ -13,6 +13,7 @@ import { MetadataFilter } from '../../domain/model'; import { SchemaService } from '../../../schema-form/service/schema.service'; import { AddFilterRequest } from '../action/collection.action'; import { CancelCreateFilter, UpdateFilterChanges, SelectFilterType } from '../action/filter.action'; +import { ActivatedRoute } from '@angular/router'; @Component({ @@ -49,7 +50,8 @@ export class NewFilterComponent implements OnDestroy, OnInit { constructor( private store: Store, private schemaService: SchemaService, - private fb: FormBuilder + private fb: FormBuilder, + private route: ActivatedRoute ) { this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); @@ -118,6 +120,6 @@ export class NewFilterComponent implements OnDestroy, OnInit { } cancel(): void { - this.store.dispatch(new CancelCreateFilter()); + this.store.dispatch(new CancelCreateFilter(this.route.snapshot.params.providerId)); } } diff --git a/ui/src/app/metadata/filter/container/select-filter.component.ts b/ui/src/app/metadata/filter/container/select-filter.component.ts index 97fe09d0f..55bdabb4e 100644 --- a/ui/src/app/metadata/filter/container/select-filter.component.ts +++ b/ui/src/app/metadata/filter/container/select-filter.component.ts @@ -8,6 +8,8 @@ import { NgbPopoverConfig } from '@ng-bootstrap/ng-bootstrap'; import { MetadataFilter } from '../../domain/model/metadata-filter'; import { SelectFilter } from '../action/collection.action'; import * as fromFilter from '../reducer'; +import { MetadataFilterEditorTypes } from '../model'; +import { SetDefinition } from '../../../wizard/action/wizard.action'; @Component({ selector: 'select-filter-page', @@ -17,6 +19,7 @@ import * as fromFilter from '../reducer'; }) export class SelectFilterComponent implements OnDestroy { actionsSubscription: Subscription; + filterSubscription: Subscription; filter$: Observable; constructor( @@ -25,15 +28,26 @@ export class SelectFilterComponent implements OnDestroy { ) { this.actionsSubscription = this.route.params.pipe( distinctUntilChanged(), - map(params => { - return new SelectFilter(params.id); - }) + map(params => new SelectFilter(params.filterId)) ).subscribe(store); this.filter$ = this.store.select(fromFilter.getSelectedFilter); + + this.filterSubscription = this.filter$.subscribe(f => { + this.setDefinition(f); + }); + } + + setDefinition(filter: MetadataFilter): void { + if (filter) { + this.store.dispatch(new SetDefinition({ + ...MetadataFilterEditorTypes.find(def => def.type === filter['@type']) + })); + } } ngOnDestroy() { this.actionsSubscription.unsubscribe(); + this.filterSubscription.unsubscribe(); } } /* istanbul ignore next */ diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index 5a87b1184..683d71b17 100644 --- a/ui/src/app/metadata/filter/effect/collection.effect.ts +++ b/ui/src/app/metadata/filter/effect/collection.effect.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Effect, Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { of } from 'rxjs'; import { switchMap, map, catchError, tap, combineLatest, skipWhile, debounceTime, withLatestFrom } from 'rxjs/operators'; @@ -162,18 +162,25 @@ export class FilterCollectionEffects { updateFilter$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.UPDATE_FILTER_REQUEST), map(action => action.payload), - withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - switchMap(([filter, providerId]) => { + switchMap((action) => { + const { filter, providerId } = action; delete filter.modifiedDate; delete filter.createdDate; + return this.filterService .update(providerId, filter) .pipe( - map(p => new UpdateFilterSuccess({ - id: p.resourceId, - changes: p + map(resp => new UpdateFilterSuccess({ + providerId, + update: { + id: resp.resourceId, + changes: resp + } })), - catchError(err => of(err.status === 409 ? new UpdateFilterConflict(filter) : new UpdateFilterFail(err))) + catchError(err => of(err.status === 409 ? new UpdateFilterConflict({ + filter, + providerId + }) : new UpdateFilterFail(err))) ); }) ); @@ -184,16 +191,18 @@ export class FilterCollectionEffects { FilterCollectionActionTypes.SET_ORDER_FILTER_SUCCESS, FilterCollectionActionTypes.REMOVE_FILTER_SUCCESS ), - withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - map(([action, providerId]) => new SelectProviderRequest(providerId)) + map((action) => { + const { payload } = action; + const { providerId } = payload; + return new SelectProviderRequest(providerId); + }) ); @Effect({ dispatch: false }) updateFilterSuccessRedirect$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS), map(action => action.payload), - withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - tap(([filter, provider]) => this.navigateToParent(provider)) + tap(({ providerId }) => this.navigateToParent(providerId)) ); @Effect() @@ -225,12 +234,11 @@ export class FilterCollectionEffects { ofType(FilterCollectionActionTypes.SET_ORDER_FILTER_REQUEST), map(action => action.payload), withLatestFrom( - this.store.select(fromProvider.getSelectedProviderId), this.store.select(fromFilter.getPluginFilterOrder) ), - switchMap(([order, providerId, pluginOrder]) => + switchMap(([{order, providerId}, pluginOrder]) => this.filterService.setOrder(providerId, [...pluginOrder, ...order]).pipe( - map(() => new SetOrderFilterSuccess()), + map(() => new SetOrderFilterSuccess(providerId)), catchError(err => of(new SetOrderFilterFail(err))) ) ) @@ -247,11 +255,11 @@ export class FilterCollectionEffects { ofType(FilterCollectionActionTypes.CHANGE_FILTER_ORDER_UP), map(action => action.payload), withLatestFrom(this.store.select(fromFilter.getAdditionalFilterOrder)), - map(([id, order]) => { + map(([{ id, providerId }, order]) => { const index = order.indexOf(id); if (index > 0) { const newOrder = array_move(order, index, index - 1); - return new SetOrderFilterRequest(newOrder); + return new SetOrderFilterRequest({ order: newOrder, providerId }); } else { return new SetOrderFilterFail(new Error(`could not change order: ${id}`)); } @@ -263,11 +271,11 @@ export class FilterCollectionEffects { ofType(FilterCollectionActionTypes.CHANGE_FILTER_ORDER_DOWN), map(action => action.payload), withLatestFrom(this.store.select(fromFilter.getAdditionalFilterOrder)), - map(([id, order]) => { + map(([{id, providerId}, order]) => { const index = order.indexOf(id); if (index < order.length - 1) { const newOrder = array_move(order, index, index + 1); - return new SetOrderFilterRequest(newOrder); + return new SetOrderFilterRequest({ order: newOrder, providerId }); } else { return new SetOrderFilterFail(new Error(`could not change order: ${id}`)); } @@ -298,6 +306,7 @@ export class FilterCollectionEffects { constructor( private actions$: Actions, private router: Router, + private route: ActivatedRoute, private filterService: MetadataFilterService, private store: Store, private i18nService: I18nService diff --git a/ui/src/app/metadata/filter/effect/editor.effect.ts b/ui/src/app/metadata/filter/effect/editor.effect.ts new file mode 100644 index 000000000..bd8eaf261 --- /dev/null +++ b/ui/src/app/metadata/filter/effect/editor.effect.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@angular/core'; +import { Effect, Actions, ofType } from '@ngrx/effects'; +import { SchemaService } from '../../../schema-form/service/schema.service'; + +import { map, switchMap, catchError, debounceTime } from 'rxjs/operators'; +import { of } from 'rxjs'; +import { + LoadSchemaRequest, + LoadSchemaSuccess, + LoadSchemaFail, + SetDefinition, + WizardActionTypes +} from '../../../wizard/action/wizard.action'; +import { ResetChanges } from '../action/filter.action'; + +import { CancelContentionAction, ContentionActionTypes } from '../../../contention/action/contention.action'; + +@Injectable() +export class EditorEffects { + + @Effect() + $loadSchemaRequest = this.actions$.pipe( + ofType(WizardActionTypes.LOAD_SCHEMA_REQUEST), + map(action => action.payload), + debounceTime(100), + switchMap((schemaPath: string) => + this.schemaService + .get(schemaPath) + .pipe( + map(schema => new LoadSchemaSuccess(schema)), + catchError(error => of(new LoadSchemaFail(error))) + ) + ) + ); + + @Effect() + $resetChanges = this.actions$.pipe( + ofType(WizardActionTypes.SET_DEFINITION), + map(() => new ResetChanges()) + ); + + @Effect() + $resetChangesOnContentionFail = this.actions$.pipe( + ofType(ContentionActionTypes.CANCEL_CONTENTION), + map(() => new ResetChanges()) + ); + + constructor( + private schemaService: SchemaService, + private actions$: Actions + ) { } +} /* istanbul ignore next */ diff --git a/ui/src/app/metadata/filter/effect/filter.effect.ts b/ui/src/app/metadata/filter/effect/filter.effect.ts index 22778ff9e..65c5f8e89 100644 --- a/ui/src/app/metadata/filter/effect/filter.effect.ts +++ b/ui/src/app/metadata/filter/effect/filter.effect.ts @@ -4,7 +4,7 @@ import { Store } from '@ngrx/store'; import { of } from 'rxjs'; import { map, switchMap, catchError, withLatestFrom, tap, combineLatest, skipWhile } from 'rxjs/operators'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import * as fromFilter from '../reducer'; import * as fromProvider from '../../provider/reducer'; @@ -22,7 +22,6 @@ import { CancelCreateFilter } from '../action/filter.action'; import { EntityIdService } from '../../domain/service/entity-id.service'; -import { MetadataProviderService } from '../../domain/service/provider.service'; import { ShowContentionAction } from '../../../contention/action/contention.action'; import { MetadataFilter } from '../../domain/model'; import { ContentionService } from '../../../contention/service/contention.service'; @@ -48,14 +47,16 @@ export class FilterEffects { ofType(FilterCollectionActionTypes.UPDATE_FILTER_CONFLICT), map(action => action.payload), withLatestFrom( - this.store.select(fromFilter.getSelectedFilter), - this.store.select(fromProvider.getSelectedProviderId) + this.store.select(fromFilter.getSelectedFilter) ), - switchMap(([filter, current, providerId]) => + switchMap(([{ providerId, filter }, current]) => this.filterService.find(providerId, filter.resourceId).pipe( map(data => new ShowContentionAction(this.contentionService.getContention(current, filter, data, { - resolve: (obj) => this.store.dispatch(new UpdateFilterRequest({ ...obj })), - reject: (obj) => this.store.dispatch(new CancelCreateFilter()) + resolve: (obj) => this.store.dispatch(new UpdateFilterRequest({ + filter: { ...obj }, + providerId + })), + reject: (obj) => this.store.dispatch(new CancelCreateFilter(providerId)) }))) ) ) @@ -64,9 +65,8 @@ export class FilterEffects { @Effect({ dispatch: false }) cancelChanges$ = this.actions$.pipe( ofType(FilterActionTypes.CANCEL_CREATE_FILTER), - withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - tap(([filter, provider]) => { - this.router.navigate(['/', 'metadata', 'provider', provider, 'filters']); + tap((providerId) => { + this.router.navigate(['/', 'metadata', 'provider', providerId, 'configuration']); }) ); @@ -74,6 +74,7 @@ export class FilterEffects { private store: Store, private actions$: Actions, private router: Router, + private route: ActivatedRoute, private idService: EntityIdService, private filterService: MetadataFilterService, private contentionService: ContentionService diff --git a/ui/src/app/metadata/filter/model/index.ts b/ui/src/app/metadata/filter/model/index.ts index 8a681ddbe..e07edc9f8 100644 --- a/ui/src/app/metadata/filter/model/index.ts +++ b/ui/src/app/metadata/filter/model/index.ts @@ -4,7 +4,7 @@ import { EntityAttributesFilterConfiguration } from './entity-attributes-configu import { NameIDFilterConfiguration } from './nameid-configuration.filter'; export const MetadataFilterTypes = { - EntityAttributes: EntityAttributesFilter, + EntityAttributes: EntityAttributesFilterConfiguration, NameIDFormat: NameIDFilter }; diff --git a/ui/src/app/metadata/filter/reducer/collection.reducer.ts b/ui/src/app/metadata/filter/reducer/collection.reducer.ts index 2db7e232e..1a3ebcb7e 100644 --- a/ui/src/app/metadata/filter/reducer/collection.reducer.ts +++ b/ui/src/app/metadata/filter/reducer/collection.reducer.ts @@ -52,7 +52,7 @@ export function reducer(state = initialState, action: FilterCollectionActionsUni } case FilterCollectionActionTypes.UPDATE_FILTER_SUCCESS: { - return adapter.updateOne(action.payload, { + return adapter.updateOne(action.payload.update, { ...state, saving: false }); diff --git a/ui/src/app/metadata/filter/reducer/editor.reducer.ts b/ui/src/app/metadata/filter/reducer/editor.reducer.ts new file mode 100644 index 000000000..0d56550b7 --- /dev/null +++ b/ui/src/app/metadata/filter/reducer/editor.reducer.ts @@ -0,0 +1,47 @@ +import { EditorActionTypes, EditorActionUnion } from '../action/editor.action'; + +export interface EditorState { + status: { [key: string]: string }; + type: string; +} + +export const initialState: EditorState = { + status: {}, + type: null +}; + +export function reducer(state = initialState, action: EditorActionUnion): EditorState { + switch (action.type) { + case EditorActionTypes.SELECT_PROVIDER_TYPE: { + return { + ...state, + type: action.payload + }; + } + case EditorActionTypes.CLEAR: { + return { + ...initialState + }; + } + case EditorActionTypes.UPDATE_STATUS: { + return { + ...state, + status: { + ...state.status, + ...action.payload + } + }; + } + default: { + return state; + } + } +} + + + +export const isEditorValid = (state: EditorState) => + !Object.keys(state.status).some(key => state.status[key] === ('INVALID')); +export const getFormStatus = (state: EditorState) => state.status; +export const getInvalidForms = (state: EditorState) => + Object.keys(state.status).filter(key => state.status[key] === 'INVALID'); diff --git a/ui/src/app/metadata/filter/reducer/index.ts b/ui/src/app/metadata/filter/reducer/index.ts index 36682ad4d..71d71707e 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -3,17 +3,20 @@ import * as fromRoot from '../../../core/reducer'; import * as fromFilter from './filter.reducer'; import * as fromSearch from './search.reducer'; import * as fromCollection from './collection.reducer'; +import * as fromEditor from './editor.reducer'; import * as utils from '../../domain/domain.util'; import { MetadataFilter } from '../../domain/model'; export interface FilterState { filter: fromFilter.FilterState; search: fromSearch.SearchState; + editor: fromEditor.EditorState; collection: fromCollection.CollectionState; } export const reducers = { filter: fromFilter.reducer, + editor: fromEditor.reducer, search: fromSearch.reducer, collection: fromCollection.reducer }; @@ -25,6 +28,7 @@ export interface State extends fromRoot.State { export const getFiltersFromStateFn = (state: FilterState) => state.filter; export const getSearchFromStateFn = (state: FilterState) => state.search; export const getCollectionFromStateFn = (state: FilterState) => state.collection; +export const getEditorStateFn = (state: FilterState) => state.editor; export const getFilterState = createFeatureSelector('filter'); @@ -33,6 +37,8 @@ export const getFilterFromState = createSelector(getFilterState, getFiltersFromS export const getSelected = createSelector(getFilterFromState, fromFilter.getSelected); export const getFilter = createSelector(getFilterFromState, fromFilter.getFilterChanges); export const getPreview = createSelector(getFilterFromState, fromFilter.getPreview); +export const getEditorState = createSelector(getFilterState, getEditorStateFn); + /* * Select pieces of Search Collection @@ -74,6 +80,15 @@ export const getPluginFilterOrder = createSelector(getFilterEntities, getCollect export const getFilterNames = createSelector(getAllFilters, (filters: MetadataFilter[]) => filters.map(f => f.name).filter(f => !!f)); +/* +Editor State +*/ + +export const getEditorIsValid = createSelector(getEditorState, fromEditor.isEditorValid); + +export const getFormStatus = createSelector(getEditorState, fromEditor.getFormStatus); +export const getInvalidEditorForms = createSelector(getEditorState, fromEditor.getInvalidForms); + /* * Combine pieces of State */ diff --git a/ui/src/app/metadata/provider/container/provider-edit.component.ts b/ui/src/app/metadata/provider/container/provider-edit.component.ts index 199071b11..d577fe3a0 100644 --- a/ui/src/app/metadata/provider/container/provider-edit.component.ts +++ b/ui/src/app/metadata/provider/container/provider-edit.component.ts @@ -1,11 +1,11 @@ import { Component, OnDestroy } from '@angular/core'; import { Router, ActivatedRoute, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { Observable, of, Subject } from 'rxjs'; -import { skipWhile, map, combineLatest, filter, takeUntil } from 'rxjs/operators'; +import { map, filter } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import * as fromWizard from '../../../wizard/reducer'; import * as fromProvider from '../reducer'; -import { ClearWizard, SetIndex, LoadSchemaRequest } from '../../../wizard/action/wizard.action'; +import { SetIndex, LoadSchemaRequest } from '../../../wizard/action/wizard.action'; import { ClearEditor } from '../action/editor.action'; import { MetadataProvider } from '../../domain/model'; import { ClearProvider } from '../action/entity.action'; diff --git a/ui/src/app/metadata/provider/container/provider-filter-list.component.ts b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts index 825ad9443..5b92e0651 100644 --- a/ui/src/app/metadata/provider/container/provider-filter-list.component.ts +++ b/ui/src/app/metadata/provider/container/provider-filter-list.component.ts @@ -33,12 +33,16 @@ export class ProviderFilterListComponent implements OnDestroy { provider$: Observable; isSaving$: Observable; + provider: MetadataProvider; + formats = NAV_FORMATS; constructor( private store: Store, private modalService: NgbModal ) { + + console.log('filter list') this.filters$ = this.store.select(fromFilter.getAdditionalFilters) as Observable; this.provider$ = this.store.select(fromProvider.getSelectedProvider).pipe(skipWhile(p => !p)); this.provider$ @@ -47,6 +51,7 @@ export class ProviderFilterListComponent implements OnDestroy { ) .subscribe(p => { this.store.dispatch(new LoadFilterRequest(p.resourceId)); + this.provider = p; }); this.store.dispatch(new SetIndex('filters')); @@ -55,15 +60,18 @@ export class ProviderFilterListComponent implements OnDestroy { } toggleEnabled(filter: MetadataFilter): void { - this.store.dispatch(new UpdateFilterRequest({ ...filter, filterEnabled: !filter.filterEnabled })); + this.store.dispatch(new UpdateFilterRequest({ + filter: { ...filter, filterEnabled: !filter.filterEnabled }, + providerId: this.provider.resourceId + })); } updateOrderUp(filter: MetadataFilter): void { - this.store.dispatch(new ChangeFilterOrderUp(filter.resourceId)); + this.store.dispatch(new ChangeFilterOrderUp({ id: filter.resourceId, providerId: this.provider.resourceId })); } updateOrderDown(filter: MetadataFilter): void { - this.store.dispatch(new ChangeFilterOrderDown(filter.resourceId)); + this.store.dispatch(new ChangeFilterOrderDown({ id: filter.resourceId, providerId: this.provider.resourceId })); } remove(id: string): void { diff --git a/ui/src/app/metadata/provider/container/provider-select.component.ts b/ui/src/app/metadata/provider/container/provider-select.component.ts index 37519a259..160ad8332 100644 --- a/ui/src/app/metadata/provider/container/provider-select.component.ts +++ b/ui/src/app/metadata/provider/container/provider-select.component.ts @@ -3,7 +3,7 @@ import { Subscription, Observable } from 'rxjs'; import { Store } from '@ngrx/store'; import { ActivatedRoute } from '@angular/router'; -import { map, distinctUntilChanged, skipWhile, filter } from 'rxjs/operators'; +import { map, filter } from 'rxjs/operators'; import { SelectProviderRequest, ClearProviderSelection } from '../action/collection.action'; import * as fromProviders from '../reducer'; import { MetadataProvider } from '../../domain/model'; @@ -37,6 +37,7 @@ export class ProviderSelectComponent implements OnDestroy { this.providerSubscription = this.provider$.subscribe(provider => { this.setDefinition(provider); }); + console.log('hi there') } setDefinition(provider: MetadataProvider): void { diff --git a/ui/src/app/metadata/provider/provider.routing.ts b/ui/src/app/metadata/provider/provider.routing.ts index df7dd3ec0..ee73ed01b 100644 --- a/ui/src/app/metadata/provider/provider.routing.ts +++ b/ui/src/app/metadata/provider/provider.routing.ts @@ -41,46 +41,46 @@ export const ProviderRoutes: Routes = [ component: ProviderSelectComponent, children: [ { - path: '', - component: FilterComponent, + path: 'edit', + component: ProviderEditComponent, children: [ + { path: '', redirectTo: 'common', pathMatch: 'prefix' }, { - path: 'filter/new', - component: NewFilterComponent, - data: { title: `Create New Filter` } - }, - { - path: 'filter/:id', - component: SelectFilterComponent, - canActivate: [], - children: [ - { - path: 'edit', - component: EditFilterComponent, - data: { title: `Edit Metadata Filter` } - } - ] + path: ':form', + component: ProviderEditStepComponent, + data: { title: `Edit Metadata Provider`, subtitle: true } } + ], + canDeactivate: [ + CanDeactivateGuard ] }, { path: 'filters', component: ProviderFilterListComponent, data: { title: `Metadata Filter List` } + } + ] + }, + { + path: ':providerId/filter', + component: FilterComponent, + children: [ + { + path: 'new', + component: NewFilterComponent, + data: { title: `Create New Filter` } }, { - path: 'edit', - component: ProviderEditComponent, + path: ':filterId', + component: SelectFilterComponent, + canActivate: [], children: [ - { path: '', redirectTo: 'common', pathMatch: 'prefix' }, { - path: ':form', - component: ProviderEditStepComponent, - data: { title: `Edit Metadata Provider`, subtitle: true } + path: 'edit', + component: EditFilterComponent, + data: { title: `Edit Metadata Filter` } } - ], - canDeactivate: [ - CanDeactivateGuard ] } ] From 15228533c287e94a9f1ead7d954c937260bba2fa Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 16 Mar 2021 12:46:18 -0700 Subject: [PATCH 2/4] Updated filter editor --- .../container/edit-filter-step.component.ts | 54 +++++++++---------- .../container/edit-filter.component.html | 14 ++--- .../filter/container/edit-filter.component.ts | 54 +++++++------------ .../filter/effect/collection.effect.ts | 9 ++-- ui/src/app/metadata/filter/filter.module.ts | 2 + .../entity-attributes-configuration.filter.ts | 10 ++-- .../app/metadata/provider/provider.routing.ts | 11 +++- 7 files changed, 71 insertions(+), 83 deletions(-) diff --git a/ui/src/app/metadata/filter/container/edit-filter-step.component.ts b/ui/src/app/metadata/filter/container/edit-filter-step.component.ts index 281b86b95..d09f78cbf 100644 --- a/ui/src/app/metadata/filter/container/edit-filter-step.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter-step.component.ts @@ -3,14 +3,14 @@ import { Store } from '@ngrx/store'; import { Subject, Observable, Subscription } from 'rxjs'; import * as fromFilter from '../reducer'; -import { MetadataFilterTypes } from '../model'; -import { FormDefinition } from '../../../wizard/model'; +import { FormDefinition, WizardStep } from '../../../wizard/model'; import { MetadataFilter } from '../../domain/model'; import { SchemaService } from '../../../schema-form/service/schema.service'; -import { UpdateFilterRequest } from '../action/collection.action'; -import { CancelCreateFilter, UpdateFilterChanges } from '../action/filter.action'; +import { UpdateFilterChanges } from '../action/filter.action'; import { PreviewEntity } from '../../domain/action/entity.action'; -import { shareReplay, map, withLatestFrom, filter, switchMap, startWith, defaultIfEmpty, takeUntil } from 'rxjs/operators'; +import { shareReplay, map, withLatestFrom, filter, switchMap, takeUntil } from 'rxjs/operators'; +import * as fromWizard from '../../../wizard/reducer'; +import { LockEditor, UnlockEditor } from '../../../wizard/action/wizard.action'; @Component({ selector: 'edit-filter-step-page', @@ -29,6 +29,7 @@ export class EditFilterStepComponent implements OnDestroy { definition$: Observable>; definition: FormDefinition; schema$: Observable; + bindings$: Observable; model$: Observable; isSaving$: Observable; @@ -37,6 +38,8 @@ export class EditFilterStepComponent implements OnDestroy { type$: Observable; validators$: Observable<{ [key: string]: any }>; + status$: Observable; + step$: Observable; actions: any; @@ -46,24 +49,23 @@ export class EditFilterStepComponent implements OnDestroy { private store: Store, private schemaService: SchemaService ) { - this.definition$ = this.store.select(fromFilter.getFilterType).pipe( - takeUntil(this.ngUnsubscribe), - filter(t => !!t), - map(t => MetadataFilterTypes[t]) - ); + this.definition$ = this.store.select(fromWizard.getWizardDefinition).pipe(filter(d => !!d)) this.defSub = this.definition$.subscribe(d => this.definition = d); - this.definition$.subscribe(console.log); + this.schema$ = this.store.select(fromWizard.getSchema); + this.bindings$ = this.definition$.pipe(map(d => d.bindings)); + + this.step$ = this.store.select(fromWizard.getCurrent); + + this.step$.subscribe(s => { + if (s && s.locked) { + this.store.dispatch(new LockEditor()); + } else { + this.store.dispatch(new UnlockEditor()); + } + }); - this.schema$ = this.definition$.pipe( - takeUntil(this.ngUnsubscribe), - filter(d => !!d), - switchMap(d => { - return this.schemaService.get(d.schema).pipe(takeUntil(this.ngUnsubscribe)); - }), - shareReplay() - ); this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); this.model$ = this.store.select(fromFilter.getSelectedFilter); this.type$ = this.model$.pipe(map(f => f && f.hasOwnProperty('@type') ? f['@type'] : '')); @@ -73,14 +75,16 @@ export class EditFilterStepComponent implements OnDestroy { this.isValid = valid.value ? valid.value.length === 0 : true; }); + this.status$ = this.store.select(fromFilter.getInvalidEditorForms); + this.validators$ = this.store.select(fromFilter.getFilterNames).pipe( takeUntil(this.ngUnsubscribe), withLatestFrom( this.store.select(fromFilter.getSelectedFilter), this.definition$ ), - map(([names, filter, definition]) => definition.getValidators( - names.filter(n => n !== filter.name) + map(([names, provider, definition]) => definition.getValidators( + names.filter(n => n !== provider.name) )) ); @@ -101,14 +105,6 @@ export class EditFilterStepComponent implements OnDestroy { this.defSub.unsubscribe(); } - save(): void { - this.store.dispatch(new UpdateFilterRequest(this.filter)); - } - - cancel(): void { - this.store.dispatch(new CancelCreateFilter()); - } - preview(id: string): void { this.store.dispatch(new PreviewEntity({ id, diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.html b/ui/src/app/metadata/filter/container/edit-filter.component.html index cf62ba24b..c003e2ddf 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.html +++ b/ui/src/app/metadata/filter/container/edit-filter.component.html @@ -12,7 +12,7 @@ -
+
@@ -22,8 +22,8 @@
- -
diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.ts b/ui/src/app/metadata/filter/container/edit-filter.component.ts index d70537a3f..6eb9e7908 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -3,17 +3,16 @@ import { Store } from '@ngrx/store'; import { Subject, Observable, Subscription } from 'rxjs'; import * as fromFilter from '../reducer'; -import { MetadataFilterTypes } from '../model'; import { FormDefinition } from '../../../wizard/model'; import { MetadataFilter } from '../../domain/model'; -import { SchemaService } from '../../../schema-form/service/schema.service'; import { UpdateFilterRequest } from '../action/collection.action'; -import { CancelCreateFilter, UpdateFilterChanges } from '../action/filter.action'; +import { CancelCreateFilter } from '../action/filter.action'; import { PreviewEntity } from '../../domain/action/entity.action'; import { NAV_FORMATS } from '../../domain/component/editor-nav.component'; -import { shareReplay, map, withLatestFrom, filter, switchMap, takeUntil } from 'rxjs/operators'; +import { map, filter } from 'rxjs/operators'; import * as fromWizard from '../../../wizard/reducer'; import { ActivatedRoute } from '@angular/router'; +import { LoadSchemaRequest, SetIndex } from '../../../wizard/action/wizard.action'; @Component({ selector: 'edit-filter-page', @@ -23,12 +22,6 @@ export class EditFilterComponent implements OnDestroy { private ngUnsubscribe: Subject = new Subject(); - valueChangeSubject = new Subject>(); - private valueChangeEmitted$ = this.valueChangeSubject.asObservable(); - - statusChangeSubject = new Subject<{ value: any[] }>(); - private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); - definition$: Observable>; definition: FormDefinition; schema$: Observable; @@ -36,13 +29,12 @@ export class EditFilterComponent implements OnDestroy { model$: Observable; isSaving$: Observable; filter: MetadataFilter; - isValid: boolean; + isValid$: Observable; + isInvalid$: Observable; type$: Observable; status$: Observable; - validators$: Observable<{ [key: string]: any }>; - actions: any; defSub: Subscription; @@ -51,7 +43,6 @@ export class EditFilterComponent implements OnDestroy { constructor( private store: Store, - private schemaService: SchemaService, private route: ActivatedRoute ) { @@ -59,34 +50,27 @@ export class EditFilterComponent implements OnDestroy { this.defSub = this.definition$.subscribe(d => this.definition = d); - this.schema$ = this.definition$.pipe( - takeUntil(this.ngUnsubscribe), - filter(d => !!d), - switchMap(d => this.schemaService.get(d.schema).pipe(takeUntil(this.ngUnsubscribe))), - shareReplay() - ); + this.store + .select(fromWizard.getCurrentWizardSchema) + .pipe(filter(s => !!s)) + .subscribe(s => { + if (s) { + this.store.dispatch(new LoadSchemaRequest(s)); + } + }); + + let startIndex$ = this.route.firstChild.params.pipe(map(p => p.form || 'filters')); + startIndex$.subscribe(index => this.store.dispatch(new SetIndex(index))); + this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); this.model$ = this.store.select(fromFilter.getSelectedFilter); this.type$ = this.model$.pipe(map(f => f && f.hasOwnProperty('@type') ? f['@type'] : '')); - this.valueChangeEmitted$.subscribe(changes => this.store.dispatch(new UpdateFilterChanges(changes.value))); - this.statusChangeEmitted$.subscribe(valid => { - this.isValid = valid.value ? valid.value.length === 0 : true; - }); - this.status$ = this.store.select(fromFilter.getInvalidEditorForms); - this.validators$ = this.store.select(fromFilter.getFilterNames).pipe( - takeUntil(this.ngUnsubscribe), - withLatestFrom( - this.store.select(fromFilter.getSelectedFilter), - this.definition$ - ), - map(([names, provider, definition]) => definition.getValidators( - names.filter(n => n !== provider.name) - )) - ); + this.isValid$ = this.store.select(fromFilter.getEditorIsValid); + this.isInvalid$ = this.isValid$.pipe(map(v => !v)); this.store .select(fromFilter.getFilter) diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index 683d71b17..9e15e691c 100644 --- a/ui/src/app/metadata/filter/effect/collection.effect.ts +++ b/ui/src/app/metadata/filter/effect/collection.effect.ts @@ -162,13 +162,16 @@ export class FilterCollectionEffects { updateFilter$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.UPDATE_FILTER_REQUEST), map(action => action.payload), - switchMap((action) => { + withLatestFrom(this.store.select(fromFilter.getSelectedFilter)), + switchMap(([action, original]) => { const { filter, providerId } = action; delete filter.modifiedDate; delete filter.createdDate; + const updates = ({ ...original, ...filter }); + return this.filterService - .update(providerId, filter) + .update(providerId, updates) .pipe( map(resp => new UpdateFilterSuccess({ providerId, @@ -178,7 +181,7 @@ export class FilterCollectionEffects { } })), catchError(err => of(err.status === 409 ? new UpdateFilterConflict({ - filter, + filter: updates, providerId }) : new UpdateFilterFail(err))) ); diff --git a/ui/src/app/metadata/filter/filter.module.ts b/ui/src/app/metadata/filter/filter.module.ts index f30d517df..6514b5a06 100644 --- a/ui/src/app/metadata/filter/filter.module.ts +++ b/ui/src/app/metadata/filter/filter.module.ts @@ -23,11 +23,13 @@ import { FormModule } from '../../schema-form/schema-form.module'; import { I18nModule } from '../../i18n/i18n.module'; import { FilterComponent } from './container/filter.component'; import { FilterListComponent } from './component/filter-list.component'; +import { EditFilterStepComponent } from './container/edit-filter-step.component'; @NgModule({ declarations: [ NewFilterComponent, EditFilterComponent, + EditFilterStepComponent, SelectFilterComponent, SearchDialogComponent, FilterComponent, diff --git a/ui/src/app/metadata/filter/model/entity-attributes-configuration.filter.ts b/ui/src/app/metadata/filter/model/entity-attributes-configuration.filter.ts index 081aed521..6117c9427 100644 --- a/ui/src/app/metadata/filter/model/entity-attributes-configuration.filter.ts +++ b/ui/src/app/metadata/filter/model/entity-attributes-configuration.filter.ts @@ -6,10 +6,14 @@ export const EntityAttributesFilterConfiguration: Wizard = { ...EntityAttributesFilter, steps: [ { - id: 'target', + id: 'common', label: 'label.target', index: 1, fields: [ + 'name', + '@type', + 'resourceId', + 'filterEnabled', 'entityAttributesFilterTarget' ] }, @@ -19,10 +23,6 @@ export const EntityAttributesFilterConfiguration: Wizard = { index: 2, initialValues: [], fields: [ - 'name', - '@type', - 'resourceId', - 'filterEnabled', 'relyingPartyOverrides' ] }, diff --git a/ui/src/app/metadata/provider/provider.routing.ts b/ui/src/app/metadata/provider/provider.routing.ts index ee73ed01b..975b9b427 100644 --- a/ui/src/app/metadata/provider/provider.routing.ts +++ b/ui/src/app/metadata/provider/provider.routing.ts @@ -9,6 +9,7 @@ import { ProviderFilterListComponent } from './container/provider-filter-list.co import { NewFilterComponent } from '../filter/container/new-filter.component'; import { SelectFilterComponent } from '../filter/container/select-filter.component'; import { EditFilterComponent } from '../filter/container/edit-filter.component'; +import { EditFilterStepComponent } from '../filter/container/edit-filter-step.component'; import { CanDeactivateGuard } from '../../core/service/can-deactivate.guard'; import { FilterComponent } from '../filter/container/filter.component'; import { AdminGuard } from '../../core/service/admin.guard'; @@ -79,7 +80,15 @@ export const ProviderRoutes: Routes = [ { path: 'edit', component: EditFilterComponent, - data: { title: `Edit Metadata Filter` } + data: { title: `Edit Metadata Filter` }, + children: [ + { path: '', redirectTo: 'common', pathMatch: 'prefix' }, + { + path: ':form', + component: EditFilterStepComponent, + data: { title: `Edit Metadata Filter`, subtitle: true } + } + ] } ] } From 98be18508836185fa4bf46ecc5eac97e7ba5ac17 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 17 Mar 2021 13:23:36 -0700 Subject: [PATCH 3/4] Fixed validation --- .../component/editor-nav.component.html | 4 +- .../domain/component/editor-nav.component.ts | 1 + .../filter/action/collection.action.ts | 10 +- .../metadata/filter/action/editor.action.ts | 8 -- .../filter/action/filter.action.spec.ts | 2 +- .../container/edit-filter-step.component.ts | 42 +++++-- .../container/edit-filter.component.html | 26 ++--- .../filter/container/edit-filter.component.ts | 15 ++- .../container/new-filter-step.component.html | 15 +++ .../container/new-filter-step.component.ts | 104 ++++++++++++++++++ .../container/new-filter.component.html | 51 +++++---- .../filter/container/new-filter.component.ts | 94 ++++++++-------- .../filter/effect/collection.effect.ts | 16 +-- .../metadata/filter/effect/filter.effect.ts | 1 + ui/src/app/metadata/filter/filter.module.ts | 2 + .../model/nameid-configuration.filter.ts | 10 +- .../metadata/filter/reducer/editor.reducer.ts | 11 +- .../metadata/filter/reducer/filter.reducer.ts | 3 +- ui/src/app/metadata/filter/reducer/index.ts | 12 +- .../container/provider-select.component.ts | 1 - .../app/metadata/provider/provider.routing.ts | 13 ++- 21 files changed, 300 insertions(+), 141 deletions(-) create mode 100644 ui/src/app/metadata/filter/container/new-filter-step.component.html create mode 100644 ui/src/app/metadata/filter/container/new-filter-step.component.ts diff --git a/ui/src/app/metadata/domain/component/editor-nav.component.html b/ui/src/app/metadata/domain/component/editor-nav.component.html index 9fe804c85..7307e79fb 100644 --- a/ui/src/app/metadata/domain/component/editor-nav.component.html +++ b/ui/src/app/metadata/domain/component/editor-nav.component.html @@ -15,7 +15,7 @@ @@ -32,7 +32,7 @@ href="" class="nav-link" [ngClass]="{'active': (currentPage$ | async) === route.path}" - [routerLink]="['../', 'edit', route.path]" + [routerLink]="['../', path, route.path]" role="button" [attr.aria-label]="route.label"> diff --git a/ui/src/app/metadata/domain/component/editor-nav.component.ts b/ui/src/app/metadata/domain/component/editor-nav.component.ts index 32653fb43..7cf57ed1b 100644 --- a/ui/src/app/metadata/domain/component/editor-nav.component.ts +++ b/ui/src/app/metadata/domain/component/editor-nav.component.ts @@ -21,6 +21,7 @@ export enum NAV_FORMATS { export class EditorNavComponent { @Input() format: string; @Input() status: string[] = []; + @Input() path: string = 'edit'; @Output() onPageSelect: EventEmitter = new EventEmitter(); diff --git a/ui/src/app/metadata/filter/action/collection.action.ts b/ui/src/app/metadata/filter/action/collection.action.ts index 11ffad3ca..8c14693e2 100644 --- a/ui/src/app/metadata/filter/action/collection.action.ts +++ b/ui/src/app/metadata/filter/action/collection.action.ts @@ -112,13 +112,19 @@ export class UpdateFilterConflict implements Action { export class AddFilterRequest implements Action { readonly type = FilterCollectionActionTypes.ADD_FILTER_REQUEST; - constructor(public payload: MetadataFilter) { } + constructor(public payload: { + filter: MetadataFilter, + providerId: string + }) { } } export class AddFilterSuccess implements Action { readonly type = FilterCollectionActionTypes.ADD_FILTER_SUCCESS; - constructor(public payload: MetadataFilter) { } + constructor(public payload: { + filter: MetadataFilter, + providerId: string + }) { } } export class AddFilterFail implements Action { diff --git a/ui/src/app/metadata/filter/action/editor.action.ts b/ui/src/app/metadata/filter/action/editor.action.ts index 7c4dba2cf..7ea0a867b 100644 --- a/ui/src/app/metadata/filter/action/editor.action.ts +++ b/ui/src/app/metadata/filter/action/editor.action.ts @@ -2,7 +2,6 @@ import { Action } from '@ngrx/store'; export enum EditorActionTypes { UPDATE_STATUS = '[Filter Editor] Update Status', - SELECT_PROVIDER_TYPE = '[Filter Editor] Select Filter Type', CLEAR = '[Filter Editor] Clear' } @@ -12,17 +11,10 @@ export class UpdateStatus implements Action { constructor(public payload: { [key: string]: string }) { } } -export class SelectFilterType implements Action { - readonly type = EditorActionTypes.SELECT_PROVIDER_TYPE; - - constructor(public payload: string) { } -} - export class ClearEditor implements Action { readonly type = EditorActionTypes.CLEAR; } export type EditorActionUnion = | UpdateStatus - | SelectFilterType | ClearEditor; diff --git a/ui/src/app/metadata/filter/action/filter.action.spec.ts b/ui/src/app/metadata/filter/action/filter.action.spec.ts index db844107e..921f12385 100644 --- a/ui/src/app/metadata/filter/action/filter.action.spec.ts +++ b/ui/src/app/metadata/filter/action/filter.action.spec.ts @@ -2,7 +2,7 @@ import { FilterActionTypes, CancelCreateFilter, SelectId } from './filter.action describe('Filter Actions', () => { it('should provide actions', () => { - expect(new CancelCreateFilter().type).toBe(FilterActionTypes.CANCEL_CREATE_FILTER); + expect(new CancelCreateFilter('id').type).toBe(FilterActionTypes.CANCEL_CREATE_FILTER); expect(new SelectId('foo').type).toBe(FilterActionTypes.SELECT_ID); }); }); diff --git a/ui/src/app/metadata/filter/container/edit-filter-step.component.ts b/ui/src/app/metadata/filter/container/edit-filter-step.component.ts index d09f78cbf..963add6bf 100644 --- a/ui/src/app/metadata/filter/container/edit-filter-step.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter-step.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy } from '@angular/core'; +import { ChangeDetectorRef, Component, OnDestroy } from '@angular/core'; import { Store } from '@ngrx/store'; import { Subject, Observable, Subscription } from 'rxjs'; @@ -8,9 +8,10 @@ import { MetadataFilter } from '../../domain/model'; import { SchemaService } from '../../../schema-form/service/schema.service'; import { UpdateFilterChanges } from '../action/filter.action'; import { PreviewEntity } from '../../domain/action/entity.action'; -import { shareReplay, map, withLatestFrom, filter, switchMap, takeUntil } from 'rxjs/operators'; +import { map, withLatestFrom, filter, takeUntil, distinctUntilChanged, skip } from 'rxjs/operators'; import * as fromWizard from '../../../wizard/reducer'; import { LockEditor, UnlockEditor } from '../../../wizard/action/wizard.action'; +import { UpdateStatus } from '../action/editor.action'; @Component({ selector: 'edit-filter-step-page', @@ -44,12 +45,13 @@ export class EditFilterStepComponent implements OnDestroy { actions: any; defSub: Subscription; + currentPage: string; constructor( private store: Store, - private schemaService: SchemaService + private ref: ChangeDetectorRef ) { - this.definition$ = this.store.select(fromWizard.getWizardDefinition).pipe(filter(d => !!d)) + this.definition$ = this.store.select(fromWizard.getWizardDefinition).pipe(filter(d => !!d)); this.defSub = this.definition$.subscribe(d => this.definition = d); @@ -70,13 +72,29 @@ export class EditFilterStepComponent implements OnDestroy { this.model$ = this.store.select(fromFilter.getSelectedFilter); this.type$ = this.model$.pipe(map(f => f && f.hasOwnProperty('@type') ? f['@type'] : '')); - this.valueChangeEmitted$.subscribe(changes => this.store.dispatch(new UpdateFilterChanges(changes.value))); - this.statusChangeEmitted$.subscribe(valid => { - this.isValid = valid.value ? valid.value.length === 0 : true; - }); + this.valueChangeEmitted$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(changes => this.store.dispatch(new UpdateFilterChanges(changes.value))); + + this.statusChangeEmitted$ + .pipe( + skip(1), + takeUntil(this.ngUnsubscribe), + withLatestFrom(this.model$), + distinctUntilChanged() + ) + .subscribe(([errors, model]) => { + this.updateStatus(errors); + }); this.status$ = this.store.select(fromFilter.getInvalidEditorForms); + this.status$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(() => { + this.ref.detach(); + setTimeout(() => { + this.ref.detectChanges(); + this.ref.reattach(); + }, 250); + }) + this.validators$ = this.store.select(fromFilter.getFilterNames).pipe( takeUntil(this.ngUnsubscribe), withLatestFrom( @@ -90,6 +108,7 @@ export class EditFilterStepComponent implements OnDestroy { this.store .select(fromFilter.getFilter) + .pipe(takeUntil(this.ngUnsubscribe)) .subscribe(filter => this.filter = filter); this.actions = { @@ -97,6 +116,13 @@ export class EditFilterStepComponent implements OnDestroy { this.preview(parameters.filterId); } }; + + this.store.select(fromWizard.getWizardIndex).pipe(takeUntil(this.ngUnsubscribe)).subscribe(i => this.currentPage = i); + } + + updateStatus(errors: any): void { + const status = { [this.currentPage]: !(errors.value) ? 'VALID' : 'INVALID' }; + this.store.dispatch(new UpdateStatus(status)); } ngOnDestroy(): void { diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.html b/ui/src/app/metadata/filter/container/edit-filter.component.html index c003e2ddf..fa9e113b5 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.html +++ b/ui/src/app/metadata/filter/container/edit-filter.component.html @@ -22,20 +22,18 @@
- - -   - - + +   +

diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.ts b/ui/src/app/metadata/filter/container/edit-filter.component.ts index 6eb9e7908..9f6bbfba7 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -1,6 +1,6 @@ import { Component, OnDestroy } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Subject, Observable, Subscription } from 'rxjs'; +import { Subject, Observable, Subscription, combineLatest } from 'rxjs'; import * as fromFilter from '../reducer'; import { FormDefinition } from '../../../wizard/model'; @@ -9,7 +9,7 @@ import { UpdateFilterRequest } from '../action/collection.action'; import { CancelCreateFilter } from '../action/filter.action'; import { PreviewEntity } from '../../domain/action/entity.action'; import { NAV_FORMATS } from '../../domain/component/editor-nav.component'; -import { map, filter } from 'rxjs/operators'; +import { map, filter, takeUntil, withLatestFrom, skip } from 'rxjs/operators'; import * as fromWizard from '../../../wizard/reducer'; import { ActivatedRoute } from '@angular/router'; import { LoadSchemaRequest, SetIndex } from '../../../wizard/action/wizard.action'; @@ -31,6 +31,7 @@ export class EditFilterComponent implements OnDestroy { filter: MetadataFilter; isValid$: Observable; isInvalid$: Observable; + cantSave$: Observable; type$: Observable; status$: Observable; @@ -40,6 +41,7 @@ export class EditFilterComponent implements OnDestroy { defSub: Subscription; formats = NAV_FORMATS; + currentPage constructor( private store: Store, @@ -52,7 +54,7 @@ export class EditFilterComponent implements OnDestroy { this.store .select(fromWizard.getCurrentWizardSchema) - .pipe(filter(s => !!s)) + .pipe(filter(s => !!s), takeUntil(this.ngUnsubscribe)) .subscribe(s => { if (s) { this.store.dispatch(new LoadSchemaRequest(s)); @@ -61,7 +63,6 @@ export class EditFilterComponent implements OnDestroy { let startIndex$ = this.route.firstChild.params.pipe(map(p => p.form || 'filters')); startIndex$.subscribe(index => this.store.dispatch(new SetIndex(index))); - this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); this.model$ = this.store.select(fromFilter.getSelectedFilter); @@ -69,8 +70,9 @@ export class EditFilterComponent implements OnDestroy { this.status$ = this.store.select(fromFilter.getInvalidEditorForms); - this.isValid$ = this.store.select(fromFilter.getEditorIsValid); + this.isValid$ = this.store.select(fromFilter.getFilterIsValid); this.isInvalid$ = this.isValid$.pipe(map(v => !v)); + this.cantSave$ = this.store.select(fromFilter.cantSaveFilter).pipe(skip(1)); this.store .select(fromFilter.getFilter) @@ -96,7 +98,8 @@ export class EditFilterComponent implements OnDestroy { })); } - cancel(): void { + cancel(event: MouseEvent): void { + event.preventDefault(); this.store.dispatch(new CancelCreateFilter(this.route.snapshot.params.providerId)); } diff --git a/ui/src/app/metadata/filter/container/new-filter-step.component.html b/ui/src/app/metadata/filter/container/new-filter-step.component.html new file mode 100644 index 000000000..f8d748c37 --- /dev/null +++ b/ui/src/app/metadata/filter/container/new-filter-step.component.html @@ -0,0 +1,15 @@ + +
+ + + {{ lock.value ? 'Locked' : 'Unlocked' }} + + For Advanced Knowledge Only +
+ +
\ No newline at end of file diff --git a/ui/src/app/metadata/filter/container/new-filter-step.component.ts b/ui/src/app/metadata/filter/container/new-filter-step.component.ts new file mode 100644 index 000000000..a927b5d8c --- /dev/null +++ b/ui/src/app/metadata/filter/container/new-filter-step.component.ts @@ -0,0 +1,104 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { FormBuilder } from '@angular/forms'; +import { Subject, Observable } from 'rxjs'; + +import { takeUntil, shareReplay, withLatestFrom, map, switchMap, filter, distinctUntilChanged } from 'rxjs/operators'; + + +import * as fromFilter from '../reducer'; +import { FormDefinition, WizardStep } from '../../../wizard/model'; +import { MetadataFilter } from '../../domain/model'; +import { SchemaService } from '../../../schema-form/service/schema.service'; +import { UpdateFilterChanges } from '../action/filter.action'; +import { ActivatedRoute } from '@angular/router'; +import * as fromWizard from '../../../wizard/reducer'; +import { SetIndex } from '../../../wizard/action/wizard.action'; +import { UpdateStatus } from '../action/editor.action'; + +@Component({ + selector: 'new-filter-step-page', + templateUrl: './new-filter-step.component.html' +}) +export class NewFilterStepComponent implements OnDestroy, OnInit { + + private ngUnsubscribe: Subject = new Subject(); + + valueChangeSubject = new Subject>(); + private valueChangeEmitted$ = this.valueChangeSubject.asObservable(); + + statusChangeSubject = new Subject<{ value: any[] }>(); + private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); + + definition$: Observable>; + schema$: Observable; + + changes$: Observable; + isSaving$: Observable; + filter: MetadataFilter; + + validators$: Observable<{ [key: string]: any }>; + + options$: Observable[]>; + + step$: Observable; + + currentPage: string; + + constructor( + private store: Store, + private schemaService: SchemaService, + private fb: FormBuilder, + private route: ActivatedRoute + ) { + this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); + this.changes$ = this.store.select(fromFilter.getFilter); + this.step$ = this.store.select(fromWizard.getCurrent); + this.definition$ = this.store.select(fromWizard.getWizardDefinition).pipe(filter(d => !!d)); + this.schema$ = this.store.select(fromWizard.getSchema); + + this.validators$ = this.definition$.pipe( + takeUntil(this.ngUnsubscribe), + withLatestFrom(this.store.select(fromFilter.getFilterNames)), + map(([definition, names]) => definition.getValidators(names)) + ); + + let startIndex$ = this.route.params.pipe(map(p => p.form || 'filters')); + startIndex$.subscribe(index => this.store.dispatch(new SetIndex(index))); + } + + ngOnInit(): void { + this.valueChangeEmitted$ + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe(changes => this.store.dispatch(new UpdateFilterChanges(changes.value))); + this.statusChangeEmitted$ + .pipe( + takeUntil(this.ngUnsubscribe), + distinctUntilChanged() + ) + .subscribe(errors => { + this.updateStatus(errors); + }); + + this.store + .select(fromFilter.getFilter) + .pipe( + takeUntil(this.ngUnsubscribe), + withLatestFrom(this.definition$), + map(([filter, definition]) => definition.parser(filter)) + ) + .subscribe(filter => this.filter = filter); + + this.store.select(fromWizard.getWizardIndex).subscribe(i => this.currentPage = i); + } + + updateStatus(errors: any): void { + const status = { [this.currentPage]: !(errors.value) ? 'VALID' : 'INVALID' }; + this.store.dispatch(new UpdateStatus(status)); + } + + ngOnDestroy(): void { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); + } +} diff --git a/ui/src/app/metadata/filter/container/new-filter.component.html b/ui/src/app/metadata/filter/container/new-filter.component.html index a30cb77fc..3976b1ff9 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.html +++ b/ui/src/app/metadata/filter/container/new-filter.component.html @@ -12,9 +12,9 @@
-
+
-
+
-
- - -   - - +
+ +   +
-
- +
+
+
+ + +
+
+ +
diff --git a/ui/src/app/metadata/filter/container/new-filter.component.ts b/ui/src/app/metadata/filter/container/new-filter.component.ts index 785473738..51907707a 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.ts +++ b/ui/src/app/metadata/filter/container/new-filter.component.ts @@ -1,20 +1,19 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { Validators, FormBuilder, FormGroup } from '@angular/forms'; -import { Subject, Observable, of } from 'rxjs'; - -import { takeUntil, shareReplay, withLatestFrom, map, switchMap, filter, startWith, distinctUntilChanged, share } from 'rxjs/operators'; - +import { Subject, Observable, of, combineLatest } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; +import { takeUntil, withLatestFrom, map, filter, distinctUntilChanged, skip } from 'rxjs/operators'; import * as fromFilter from '../reducer'; -import { MetadataFilterTypes } from '../model'; +import { MetadataFilterEditorTypes, MetadataFilterTypes } from '../model'; import { FormDefinition } from '../../../wizard/model'; import { MetadataFilter } from '../../domain/model'; -import { SchemaService } from '../../../schema-form/service/schema.service'; import { AddFilterRequest } from '../action/collection.action'; -import { CancelCreateFilter, UpdateFilterChanges, SelectFilterType } from '../action/filter.action'; -import { ActivatedRoute } from '@angular/router'; - +import { CancelCreateFilter, SelectFilterType } from '../action/filter.action'; +import * as fromWizard from '../../../wizard/reducer'; +import { LoadSchemaRequest, SetDefinition, SetIndex } from '../../../wizard/action/wizard.action'; +import { NAV_FORMATS } from '../../domain/component/editor-nav.component'; @Component({ selector: 'new-filter-page', @@ -24,20 +23,14 @@ export class NewFilterComponent implements OnDestroy, OnInit { private ngUnsubscribe: Subject = new Subject(); - valueChangeSubject = new Subject>(); - private valueChangeEmitted$ = this.valueChangeSubject.asObservable(); - - statusChangeSubject = new Subject<{ value: any[] }>(); - private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); - definition$: Observable>; schema$: Observable; - changes$: Observable; - model: any; isSaving$: Observable; filter: MetadataFilter; - isValid: boolean; + isValid$: Observable; + isInvalid$: Observable; + cantSave$: Observable; validators$: Observable<{ [key: string]: any }>; @@ -47,17 +40,22 @@ export class NewFilterComponent implements OnDestroy, OnInit { options$: Observable[]>; + formats = NAV_FORMATS; + + status$: Observable; + + type$: Observable = this.store.select(fromFilter.getFilterType); + constructor( private store: Store, - private schemaService: SchemaService, private fb: FormBuilder, private route: ActivatedRoute ) { this.isSaving$ = this.store.select(fromFilter.getCollectionSaving); + this.isValid$ = this.store.select(fromFilter.getFilterIsValid); + this.isInvalid$ = this.isValid$.pipe(map(v => !v)); - this.changes$ = this.store.select(fromFilter.getFilter); - - this.model = {}; + this.cantSave$ = this.store.select(fromFilter.cantSaveFilter).pipe(skip(1)); this.definition$ = this.store.select(fromFilter.getFilterType).pipe( takeUntil(this.ngUnsubscribe), @@ -65,21 +63,19 @@ export class NewFilterComponent implements OnDestroy, OnInit { map(t => MetadataFilterTypes[t]) ); - this.schema$ = this.definition$.pipe( - takeUntil(this.ngUnsubscribe), - filter(d => !!d), - switchMap(d => { - return this.schemaService.get(d.schema).pipe(takeUntil(this.ngUnsubscribe)); - }), - shareReplay() - ); + this.store + .select(fromWizard.getCurrentWizardSchema) + .pipe(filter(s => !!s), takeUntil(this.ngUnsubscribe)) + .subscribe(s => { + if (s) { + this.store.dispatch(new LoadSchemaRequest(s)); + } + }); - this.validators$ = this.definition$.pipe( - takeUntil(this.ngUnsubscribe), - withLatestFrom(this.store.select(fromFilter.getFilterNames)), - map(([definition, names]) => definition.getValidators(names)) - ); + let startIndex$ = this.route.params.pipe(map(p => p.form || 'filters'), takeUntil(this.ngUnsubscribe)); + startIndex$.subscribe(index => this.store.dispatch(new SetIndex(index))); + this.status$ = this.store.select(fromFilter.getInvalidEditorForms); this.options$ = of(Object.values(MetadataFilterTypes)); this.form.get('type').valueChanges @@ -87,19 +83,22 @@ export class NewFilterComponent implements OnDestroy, OnInit { takeUntil(this.ngUnsubscribe), distinctUntilChanged() ) - .subscribe(type => this.store.dispatch(new SelectFilterType(type))); + .subscribe(type => { + this.store.dispatch(new SelectFilterType(type)); + }); + + this.type$.pipe( + takeUntil(this.ngUnsubscribe) + ).subscribe((t: string) => { + if (t) { + this.store.dispatch(new SetDefinition({ + ...MetadataFilterEditorTypes.find(def => def.type === t) + })); + } + }); } ngOnInit(): void { - this.valueChangeEmitted$ - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(changes => this.store.dispatch(new UpdateFilterChanges(changes.value))); - this.statusChangeEmitted$ - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe(valid => { - this.isValid = valid.value ? valid.value.length === 0 : true; - }); - this.store .select(fromFilter.getFilter) .pipe( @@ -116,7 +115,10 @@ export class NewFilterComponent implements OnDestroy, OnInit { } save(): void { - this.store.dispatch(new AddFilterRequest(this.filter)); + this.store.dispatch(new AddFilterRequest({ + filter: this.filter, + providerId: this.route.snapshot.params.providerId + })); } cancel(): void { diff --git a/ui/src/app/metadata/filter/effect/collection.effect.ts b/ui/src/app/metadata/filter/effect/collection.effect.ts index 9e15e691c..db94eacaf 100644 --- a/ui/src/app/metadata/filter/effect/collection.effect.ts +++ b/ui/src/app/metadata/filter/effect/collection.effect.ts @@ -90,12 +90,14 @@ export class FilterCollectionEffects { addFilter$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.ADD_FILTER_REQUEST), map(action => action.payload), - withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - switchMap(([unsaved, providerId]) => { + switchMap(({filter, providerId}) => { return this.filterService - .save(providerId, unsaved as MetadataFilter) + .save(providerId, filter as MetadataFilter) .pipe( - map(saved => new AddFilterSuccess(saved)), + map(saved => new AddFilterSuccess({ + filter: saved, + providerId: providerId + })), catchError(error => of(new AddFilterFail(error))) ); }) @@ -104,16 +106,14 @@ export class FilterCollectionEffects { addFilterSuccessRedirect$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.ADD_FILTER_SUCCESS), map(action => action.payload), - withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - tap(([filter, provider]) => this.navigateToParent(provider)) + tap(({ providerId }) => this.navigateToParent(providerId)) ); @Effect() addFilterSuccessReloadParent$ = this.actions$.pipe( ofType(FilterCollectionActionTypes.ADD_FILTER_SUCCESS), map(action => action.payload), - withLatestFrom(this.store.select(fromProvider.getSelectedProviderId).pipe(skipWhile(id => !id))), - map(([filter, provider]) => new SelectProviderRequest(provider)) + map(({ providerId }) => new SelectProviderRequest(providerId)) ); @Effect() diff --git a/ui/src/app/metadata/filter/effect/filter.effect.ts b/ui/src/app/metadata/filter/effect/filter.effect.ts index 65c5f8e89..2bdc60bc9 100644 --- a/ui/src/app/metadata/filter/effect/filter.effect.ts +++ b/ui/src/app/metadata/filter/effect/filter.effect.ts @@ -65,6 +65,7 @@ export class FilterEffects { @Effect({ dispatch: false }) cancelChanges$ = this.actions$.pipe( ofType(FilterActionTypes.CANCEL_CREATE_FILTER), + map(action => action.payload), tap((providerId) => { this.router.navigate(['/', 'metadata', 'provider', providerId, 'configuration']); }) diff --git a/ui/src/app/metadata/filter/filter.module.ts b/ui/src/app/metadata/filter/filter.module.ts index 6514b5a06..e7c3bfcff 100644 --- a/ui/src/app/metadata/filter/filter.module.ts +++ b/ui/src/app/metadata/filter/filter.module.ts @@ -24,10 +24,12 @@ import { I18nModule } from '../../i18n/i18n.module'; import { FilterComponent } from './container/filter.component'; import { FilterListComponent } from './component/filter-list.component'; import { EditFilterStepComponent } from './container/edit-filter-step.component'; +import { NewFilterStepComponent } from './container/new-filter-step.component'; @NgModule({ declarations: [ NewFilterComponent, + NewFilterStepComponent, EditFilterComponent, EditFilterStepComponent, SelectFilterComponent, diff --git a/ui/src/app/metadata/filter/model/nameid-configuration.filter.ts b/ui/src/app/metadata/filter/model/nameid-configuration.filter.ts index 4ff615ac6..5520f4919 100644 --- a/ui/src/app/metadata/filter/model/nameid-configuration.filter.ts +++ b/ui/src/app/metadata/filter/model/nameid-configuration.filter.ts @@ -6,10 +6,14 @@ export const NameIDFilterConfiguration: Wizard = { ...NameIDFilter, steps: [ { - id: 'target', + id: 'common', label: 'label.target', index: 1, fields: [ + 'name', + 'filterEnabled', + '@type', + 'resourceId', 'nameIdFormatFilterTarget' ] }, @@ -19,10 +23,6 @@ export const NameIDFilterConfiguration: Wizard = { index: 1, initialValues: [], fields: [ - 'name', - 'filterEnabled', - '@type', - 'resourceId', 'removeExistingFormats', 'formats' ] diff --git a/ui/src/app/metadata/filter/reducer/editor.reducer.ts b/ui/src/app/metadata/filter/reducer/editor.reducer.ts index 0d56550b7..dcb0620f4 100644 --- a/ui/src/app/metadata/filter/reducer/editor.reducer.ts +++ b/ui/src/app/metadata/filter/reducer/editor.reducer.ts @@ -2,28 +2,21 @@ import { EditorActionTypes, EditorActionUnion } from '../action/editor.action'; export interface EditorState { status: { [key: string]: string }; - type: string; } export const initialState: EditorState = { - status: {}, - type: null + status: {} }; export function reducer(state = initialState, action: EditorActionUnion): EditorState { switch (action.type) { - case EditorActionTypes.SELECT_PROVIDER_TYPE: { - return { - ...state, - type: action.payload - }; - } case EditorActionTypes.CLEAR: { return { ...initialState }; } case EditorActionTypes.UPDATE_STATUS: { + //console.log(action) return { ...state, status: { diff --git a/ui/src/app/metadata/filter/reducer/filter.reducer.ts b/ui/src/app/metadata/filter/reducer/filter.reducer.ts index 68aaf40ed..bda4668ca 100644 --- a/ui/src/app/metadata/filter/reducer/filter.reducer.ts +++ b/ui/src/app/metadata/filter/reducer/filter.reducer.ts @@ -39,13 +39,14 @@ export function reducer(state = initialState, action: FilterActionsUnion): Filte }; } case FilterActionTypes.UPDATE_FILTER: { - return { + const s = { ...state, changes: { ...state.changes, ...action.payload } }; + return s; } case FilterActionTypes.CLEAR_FILTER: case FilterActionTypes.CANCEL_CREATE_FILTER: { diff --git a/ui/src/app/metadata/filter/reducer/index.ts b/ui/src/app/metadata/filter/reducer/index.ts index 71d71707e..ee2da9bf3 100644 --- a/ui/src/app/metadata/filter/reducer/index.ts +++ b/ui/src/app/metadata/filter/reducer/index.ts @@ -85,10 +85,10 @@ Editor State */ export const getEditorIsValid = createSelector(getEditorState, fromEditor.isEditorValid); - export const getFormStatus = createSelector(getEditorState, fromEditor.getFormStatus); export const getInvalidEditorForms = createSelector(getEditorState, fromEditor.getInvalidForms); + /* * Combine pieces of State */ @@ -97,8 +97,12 @@ export const mergeFn = (changes, filter) => ({ ...filter, ...changes }); export const detectFilterType = (changes) => changes.type ? changes.type : changes.hasOwnProperty('@type') ? changes['@type'] : null; export const getFilterWithChanges = createSelector(getFilter, getSelectedFilter, mergeFn); -export const getFilterType = createSelector(getFilter, (changes: MetadataFilter) => { - const type = changes ? detectFilterType(changes) : null; - return type; +export const getFilterType = createSelector(getFilter, (changes: MetadataFilter) => changes ? detectFilterType(changes) : null); + +export const getFilterIsValid = createSelector(getEditorIsValid, getFilterType, getFormStatus, (isValid, type, status) => { + return isValid && type !== null && Object.keys(status).length > 0; }); +export const cantSaveFilter = createSelector(getCollectionSaving, getFilterIsValid, (isSaving, isValid) => { + return (isSaving || !isValid); +}); \ No newline at end of file diff --git a/ui/src/app/metadata/provider/container/provider-select.component.ts b/ui/src/app/metadata/provider/container/provider-select.component.ts index 160ad8332..6e300d302 100644 --- a/ui/src/app/metadata/provider/container/provider-select.component.ts +++ b/ui/src/app/metadata/provider/container/provider-select.component.ts @@ -37,7 +37,6 @@ export class ProviderSelectComponent implements OnDestroy { this.providerSubscription = this.provider$.subscribe(provider => { this.setDefinition(provider); }); - console.log('hi there') } setDefinition(provider: MetadataProvider): void { diff --git a/ui/src/app/metadata/provider/provider.routing.ts b/ui/src/app/metadata/provider/provider.routing.ts index 975b9b427..635edc45f 100644 --- a/ui/src/app/metadata/provider/provider.routing.ts +++ b/ui/src/app/metadata/provider/provider.routing.ts @@ -14,6 +14,7 @@ import { CanDeactivateGuard } from '../../core/service/can-deactivate.guard'; import { FilterComponent } from '../filter/container/filter.component'; import { AdminGuard } from '../../core/service/admin.guard'; import { MetadataProviderPageComponent } from './provider.component'; +import { NewFilterStepComponent } from '../filter/container/new-filter-step.component'; export const ProviderRoutes: Routes = [ { @@ -33,7 +34,7 @@ export const ProviderRoutes: Routes = [ { path: 'new', component: ProviderWizardStepComponent, - data: { title: `Create Provider`, subtitle: true } + data: { title: `Create Provider`, subtitle: true }, } ] }, @@ -70,7 +71,15 @@ export const ProviderRoutes: Routes = [ { path: 'new', component: NewFilterComponent, - data: { title: `Create New Filter` } + data: { title: `Create New Filter` }, + children: [ + { path: '', redirectTo: 'common', pathMatch: 'prefix' }, + { + path: ':form', + component: NewFilterStepComponent, + data: { title: `Create Filter`, subtitle: true } + } + ] }, { path: ':filterId', From af8c08a3c21bbadbfa9086ad87ea753667df4307 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 17 Mar 2021 14:37:59 -0700 Subject: [PATCH 4/4] fixed test --- ui/karma.conf.js | 6 +- ui/package-lock.json | 213 +++++++----------- ui/package.json | 2 +- .../container/edit-filter.component.html | 4 +- .../container/edit-filter.component.spec.ts | 40 ++-- .../filter/container/edit-filter.component.ts | 13 +- .../container/new-filter.component.spec.ts | 39 ++-- .../filter/reducer/collection.reducer.spec.ts | 20 +- .../filter/reducer/filter.reducer.spec.ts | 2 +- .../filter/reducer/search.reducer.spec.ts | 10 +- ui/src/testing/activated-route.stub.ts | 3 + 11 files changed, 152 insertions(+), 200 deletions(-) diff --git a/ui/karma.conf.js b/ui/karma.conf.js index b69d2eda1..347949dd4 100644 --- a/ui/karma.conf.js +++ b/ui/karma.conf.js @@ -10,14 +10,14 @@ module.exports = function (config) { require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), + require('karma-coverage'), require('karma-spec-reporter'), require('@angular-devkit/build-angular/plugins/karma') ], client: { clearContext: false // leave Jasmine Spec Runner output visible in browser }, - coverageIstanbulReporter: { + coverageReporter: { dir: require('path').join(__dirname, 'coverage'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true, skipFilesWithNoCoverage: false, @@ -41,7 +41,7 @@ module.exports = function (config) { angularCli: { environment: 'dev' }, - reporters: ['spec', 'coverage-istanbul'], + reporters: ['spec', 'coverage'], port: 9876, colors: true, logLevel: config.LOG_WARN, diff --git a/ui/package-lock.json b/ui/package-lock.json index 922d700dd..12b4ccd70 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1323,7 +1323,6 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/generator": "^7.12.10", @@ -1346,7 +1345,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -1355,7 +1353,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "dev": true, "requires": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -1366,7 +1363,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -1377,7 +1373,6 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -1386,7 +1381,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, "requires": { "@babel/types": "^7.12.11" } @@ -1395,7 +1389,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -1405,14 +1398,12 @@ "@babel/parser": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "dev": true + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==" }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -1423,7 +1414,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "dev": true, "requires": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -1440,7 +1430,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -1451,7 +1440,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -1460,7 +1448,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -1468,14 +1455,12 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -1882,7 +1867,6 @@ "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", - "dev": true, "requires": { "@babel/types": "^7.12.7" }, @@ -1891,7 +1875,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -1904,7 +1887,6 @@ "version": "7.12.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "dev": true, "requires": { "@babel/types": "^7.12.5" }, @@ -1913,7 +1895,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -1926,7 +1907,6 @@ "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "dev": true, "requires": { "@babel/helper-module-imports": "^7.12.1", "@babel/helper-replace-supers": "^7.12.1", @@ -1943,7 +1923,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -1952,7 +1931,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "dev": true, "requires": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -1963,7 +1941,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -1974,7 +1951,6 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -1983,7 +1959,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, "requires": { "@babel/types": "^7.12.11" } @@ -1992,7 +1967,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -2002,14 +1976,12 @@ "@babel/parser": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "dev": true + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==" }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -2020,7 +1992,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "dev": true, "requires": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -2037,7 +2008,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -2048,7 +2018,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -2056,14 +2025,12 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -2071,7 +2038,6 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "dev": true, "requires": { "@babel/types": "^7.12.10" }, @@ -2080,7 +2046,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -2123,7 +2088,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", - "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.12.7", "@babel/helper-optimise-call-expression": "^7.12.10", @@ -2135,7 +2099,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -2144,7 +2107,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "dev": true, "requires": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -2155,7 +2117,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -2166,7 +2127,6 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -2175,7 +2135,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, "requires": { "@babel/types": "^7.12.11" } @@ -2184,7 +2143,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -2194,14 +2152,12 @@ "@babel/parser": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "dev": true + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==" }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -2212,7 +2168,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "dev": true, "requires": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -2229,7 +2184,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -2240,7 +2194,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -2248,14 +2201,12 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -2263,7 +2214,6 @@ "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "dev": true, "requires": { "@babel/types": "^7.12.1" }, @@ -2272,7 +2222,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -2328,8 +2277,7 @@ "@babel/helper-validator-identifier": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==" }, "@babel/helper-validator-option": { "version": "7.12.11", @@ -2481,7 +2429,6 @@ "version": "7.12.5", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", - "dev": true, "requires": { "@babel/template": "^7.10.4", "@babel/traverse": "^7.12.5", @@ -2492,7 +2439,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -2501,7 +2447,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "dev": true, "requires": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -2512,7 +2457,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -2523,7 +2467,6 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -2532,7 +2475,6 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "dev": true, "requires": { "@babel/types": "^7.12.11" } @@ -2541,7 +2483,6 @@ "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -2551,14 +2492,12 @@ "@babel/parser": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", - "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "dev": true + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==" }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -2569,7 +2508,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "dev": true, "requires": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -2586,7 +2524,6 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -2597,7 +2534,6 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -2605,14 +2541,12 @@ "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -3614,8 +3548,7 @@ "@istanbuljs/schema": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==" }, "@jsdevtools/coverage-istanbul-loader": { "version": "3.0.5", @@ -4606,7 +4539,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -4925,8 +4857,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -4989,7 +4920,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -5234,7 +5166,6 @@ "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5647,7 +5578,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.2.tgz", "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==", - "dev": true, "requires": { "ansi-styles": "^3.1.0", "escape-string-regexp": "^1.0.5", @@ -6025,7 +5955,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", - "dev": true, "requires": { "color-name": "^1.1.1" } @@ -6033,8 +5962,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { "version": "1.5.4", @@ -6179,8 +6107,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.0", @@ -8038,8 +7965,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint-scope": { "version": "4.0.3", @@ -9002,8 +8928,7 @@ "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" }, "get-caller-file": { "version": "2.0.5", @@ -9098,8 +9023,7 @@ "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "globby": { "version": "11.0.2", @@ -9265,8 +9189,7 @@ "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" }, "has-symbols": { "version": "1.0.0", @@ -9464,8 +9387,7 @@ "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==" }, "http-cache-semantics": { "version": "4.1.0", @@ -10220,14 +10142,12 @@ "istanbul-lib-coverage": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==" }, "istanbul-lib-instrument": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, "requires": { "@babel/core": "^7.7.5", "@istanbuljs/schema": "^0.1.2", @@ -10238,8 +10158,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -10247,7 +10166,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, "requires": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^3.0.0", @@ -10257,14 +10175,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -10356,7 +10272,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, "requires": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -10439,8 +10354,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "3.14.1", @@ -10504,7 +10418,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, "requires": { "minimist": "^1.2.5" } @@ -10844,6 +10757,49 @@ "resolve": "^1.3.3" } }, + "karma-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.0.3.tgz", + "integrity": "sha512-atDvLQqvPcLxhED0cmXYdsPMCQuh6Asa9FMZW1bhNqlVEhJoB9qyZ2BY1gu7D/rr5GLGb5QzYO4siQskxaWP/g==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.1", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", + "minimatch": "^3.0.4" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "karma-coverage-istanbul-reporter": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", @@ -11124,8 +11080,7 @@ "lodash": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash.get": { "version": "4.4.2", @@ -11278,7 +11233,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, "requires": { "semver": "^6.0.0" }, @@ -11286,8 +11240,7 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -11675,7 +11628,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11683,8 +11635,7 @@ "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "minipass": { "version": "3.1.3", @@ -16649,8 +16600,7 @@ "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "safe-regex": { "version": "1.1.0", @@ -16838,8 +16788,7 @@ "semver": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", - "dev": true + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" }, "semver-dsl": { "version": "1.0.1", @@ -17186,7 +17135,8 @@ }, "kind-of": { "version": "6.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true } } @@ -17427,8 +17377,7 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-loader": { "version": "1.1.3", @@ -18171,7 +18120,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, "requires": { "has-flag": "^2.0.0" } @@ -18593,8 +18541,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-object-path": { "version": "0.3.0", diff --git a/ui/package.json b/ui/package.json index 7b4de1ad9..320fb8095 100644 --- a/ui/package.json +++ b/ui/package.json @@ -65,9 +65,9 @@ "karma": "~5.0.0", "karma-chrome-launcher": "~3.1.0", "karma-cli": "^2.0.0", - "karma-coverage-istanbul-reporter": "~3.0.2", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", + "karma-coverage": "^2.0.3", "karma-spec-reporter": "0.0.32", "ncp": "^2.0.0", "node-sass": "^4.14.1", diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.html b/ui/src/app/metadata/filter/container/edit-filter.component.html index fa9e113b5..394c27aea 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.html +++ b/ui/src/app/metadata/filter/container/edit-filter.component.html @@ -22,7 +22,7 @@
-   -
diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts b/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts index 660bf53fb..9d7e31a46 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.spec.ts @@ -2,22 +2,32 @@ import { TestBed, ComponentFixture } from '@angular/core/testing'; import { ReactiveFormsModule, FormBuilder } from '@angular/forms'; import { StoreModule, Store, combineReducers } from '@ngrx/store'; import * as fromFilter from '../reducer'; +import * as fromWizard from '../../../wizard/reducer'; import { ProviderStatusEmitter, ProviderValueEmitter } from '../../domain/service/provider-change-emitter.service'; import { NgbPopoverModule, NgbPopoverConfig } from '@ng-bootstrap/ng-bootstrap'; import { NavigatorService } from '../../../core/service/navigator.service'; import { SharedModule } from '../../../shared/shared.module'; import { EditFilterComponent } from './edit-filter.component'; -import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-schema-form'; +import { SchemaFormModule } from 'ngx-schema-form'; import { SchemaService } from '../../../schema-form/service/schema.service'; import { HttpClientModule } from '@angular/common/http'; import { MockI18nModule } from '../../../../testing/i18n.stub'; import { MetadataFilterTypes } from '../model'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ActivatedRouteStub } from '../../../../testing/activated-route.stub'; +import { ActivatedRoute } from '@angular/router'; describe('Edit Metadata Filter Page', () => { let fixture: ComponentFixture; let store: Store; let instance: EditFilterComponent; + let activatedRoute: ActivatedRouteStub = new ActivatedRouteStub(); + activatedRoute.testParamMap = { providerId: 'foo' }; + let child: ActivatedRouteStub = new ActivatedRouteStub(); + child.testParamMap = { form: 'common' }; + activatedRoute.firstChild = child; + beforeEach(() => { TestBed.configureTestingModule({ providers: [ @@ -26,11 +36,15 @@ describe('Edit Metadata Filter Page', () => { FormBuilder, NgbPopoverConfig, NavigatorService, - SchemaService + SchemaService, + { + provide: ActivatedRoute, useValue: activatedRoute + } ], imports: [ StoreModule.forRoot({ 'filter': combineReducers(fromFilter.reducers), + 'wizard': combineReducers(fromWizard.reducers) }), ReactiveFormsModule, NgbPopoverModule, @@ -60,7 +74,7 @@ describe('Edit Metadata Filter Page', () => { describe('cancel method', () => { it('should dispatch a cancel changes action', () => { fixture.detectChanges(); - instance.cancel(); + instance.cancel('foo'); expect(store.dispatch).toHaveBeenCalled(); }); }); @@ -73,24 +87,4 @@ describe('Edit Metadata Filter Page', () => { expect(store.dispatch).toHaveBeenCalled(); }); }); - - describe('status emitter', () => { - it('should set the isValid property to true', () => { - fixture.detectChanges(); - instance.statusChangeSubject.next({value: []}); - expect(instance.isValid).toBe(true); - }); - - it('should set the isValid property to true if value is undefined', () => { - fixture.detectChanges(); - instance.statusChangeSubject.next({value: null}); - expect(instance.isValid).toBe(true); - }); - - it('should set the isValid property to false', () => { - fixture.detectChanges(); - instance.statusChangeSubject.next({ value: [{control: 'foo'}] }); - expect(instance.isValid).toBe(false); - }); - }); }); diff --git a/ui/src/app/metadata/filter/container/edit-filter.component.ts b/ui/src/app/metadata/filter/container/edit-filter.component.ts index 9f6bbfba7..1df2ac5d6 100644 --- a/ui/src/app/metadata/filter/container/edit-filter.component.ts +++ b/ui/src/app/metadata/filter/container/edit-filter.component.ts @@ -41,7 +41,7 @@ export class EditFilterComponent implements OnDestroy { defSub: Subscription; formats = NAV_FORMATS; - currentPage + providerId: string; constructor( private store: Store, @@ -52,6 +52,8 @@ export class EditFilterComponent implements OnDestroy { this.defSub = this.definition$.subscribe(d => this.definition = d); + this.providerId = this.route.snapshot.params.providerId; + this.store .select(fromWizard.getCurrentWizardSchema) .pipe(filter(s => !!s), takeUntil(this.ngUnsubscribe)) @@ -91,16 +93,15 @@ export class EditFilterComponent implements OnDestroy { this.defSub.unsubscribe(); } - save(): void { + save(id: string): void { this.store.dispatch(new UpdateFilterRequest({ filter: this.filter, - providerId: this.route.snapshot.params.providerId + providerId: id })); } - cancel(event: MouseEvent): void { - event.preventDefault(); - this.store.dispatch(new CancelCreateFilter(this.route.snapshot.params.providerId)); + cancel(id: string): void { + this.store.dispatch(new CancelCreateFilter(id)); } preview(id: string): void { diff --git a/ui/src/app/metadata/filter/container/new-filter.component.spec.ts b/ui/src/app/metadata/filter/container/new-filter.component.spec.ts index b872f2f09..18c7e067e 100644 --- a/ui/src/app/metadata/filter/container/new-filter.component.spec.ts +++ b/ui/src/app/metadata/filter/container/new-filter.component.spec.ts @@ -3,6 +3,7 @@ import { ReactiveFormsModule, FormBuilder } from '@angular/forms'; import { StoreModule, Store, combineReducers } from '@ngrx/store'; import { NewFilterComponent } from './new-filter.component'; import * as fromFilter from '../reducer'; +import * as fromWizard from '../../../wizard/reducer'; import { ProviderStatusEmitter, ProviderValueEmitter } from '../../domain/service/provider-change-emitter.service'; import { NgbPopoverModule, NgbPopoverConfig } from '@ng-bootstrap/ng-bootstrap'; import { NavigatorService } from '../../../core/service/navigator.service'; @@ -11,12 +12,18 @@ import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-sch import { SchemaService } from '../../../schema-form/service/schema.service'; import { HttpClientModule } from '@angular/common/http'; import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { RouterTestingModule } from '@angular/router/testing'; +import { ActivatedRoute } from '@angular/router'; +import { ActivatedRouteStub } from '../../../../testing/activated-route.stub'; describe('New Metadata Filter Page', () => { let fixture: ComponentFixture; let store: Store; let instance: NewFilterComponent; + let activatedRoute: ActivatedRouteStub = new ActivatedRouteStub(); + activatedRoute.testParamMap = { providerId: 'foo' }; + beforeEach(() => { TestBed.configureTestingModule({ providers: [ @@ -26,18 +33,23 @@ describe('New Metadata Filter Page', () => { NgbPopoverConfig, NavigatorService, SchemaService, - { provide: WidgetRegistry, useClass: DefaultWidgetRegistry } + { provide: WidgetRegistry, useClass: DefaultWidgetRegistry }, + { + provide: ActivatedRoute, useValue: activatedRoute + } ], imports: [ StoreModule.forRoot({ 'filter': combineReducers(fromFilter.reducers), + 'wizard': combineReducers(fromWizard.reducers) }), ReactiveFormsModule, NgbPopoverModule, SharedModule, HttpClientModule, SchemaFormModule.forRoot(), - MockI18nModule + MockI18nModule, + RouterTestingModule ], declarations: [ NewFilterComponent @@ -64,27 +76,4 @@ describe('New Metadata Filter Page', () => { expect(store.dispatch).toHaveBeenCalled(); }); }); - - describe('status emitter', () => { - it('should set the isValid property to true', () => { - fixture.detectChanges(); - instance.statusChangeSubject.next({ value: [] }); - fixture.detectChanges(); - expect(instance.isValid).toBe(true); - }); - - it('should set the isValid property to true if value is undefined', () => { - fixture.detectChanges(); - instance.statusChangeSubject.next({ value: null }); - fixture.detectChanges(); - expect(instance.isValid).toBe(true); - }); - - it('should set the isValid property to false', () => { - fixture.detectChanges(); - instance.statusChangeSubject.next({ value: [{ control: 'foo' }] }); - fixture.detectChanges(); - expect(instance.isValid).toBe(false); - }); - }); }); diff --git a/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts index d044e66df..5c11032cd 100644 --- a/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/collection.reducer.spec.ts @@ -61,14 +61,20 @@ describe('Filter Reducer', () => { describe(`${FilterCollectionActionTypes.ADD_FILTER_REQUEST}`, () => { it('should set saving to true', () => { const filter = new EntityAttributesFilterEntity({ resourceId: 'foo', createdDate: new Date().toLocaleDateString() }); - const action = new AddFilterRequest(filter); + const action = new AddFilterRequest({ + filter, + providerId: 'foo' + }); expect(reducer(snapshot, action).saving).toBe(true); }); }); describe(`${FilterCollectionActionTypes.UPDATE_FILTER_REQUEST}`, () => { it('should set saving to true', () => { const filter = new EntityAttributesFilterEntity({ resourceId: 'foo', createdDate: new Date().toLocaleDateString() }); - const action = new UpdateFilterRequest(filter); + const action = new UpdateFilterRequest({ + filter, + providerId: 'foo' + }); expect(reducer(snapshot, action).saving).toBe(true); }); }); @@ -76,7 +82,10 @@ describe('Filter Reducer', () => { describe(`${FilterCollectionActionTypes.ADD_FILTER_SUCCESS}`, () => { it('should set saving to false', () => { const filter = new EntityAttributesFilterEntity({ resourceId: 'foo', createdDate: new Date().toLocaleDateString() }); - const action = new AddFilterSuccess(filter); + const action = new AddFilterSuccess({ + filter, + providerId: 'foo' + }); expect(reducer(snapshot, action).saving).toBe(false); }); }); @@ -124,7 +133,10 @@ describe('Filter Reducer', () => { id: 'foo', changes: new EntityAttributesFilterEntity({ resourceId: 'foo', name: 'bar', createdDate: new Date().toLocaleDateString() }), }; - const action = new UpdateFilterSuccess(update); + const action = new UpdateFilterSuccess({ + update, + providerId: 'foo' + }); const result = reducer(snapshot, action); expect(fromFilter.adapter.updateOne).toHaveBeenCalled(); }); diff --git a/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts index 229478811..3689e9f16 100644 --- a/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/filter.reducer.spec.ts @@ -49,7 +49,7 @@ describe('Filter Reducer', () => { }); describe(`${FilterActionTypes.CANCEL_CREATE_FILTER} action`, () => { it('should set saving to true', () => { - const result = reducer(snapshot, new CancelCreateFilter()); + const result = reducer(snapshot, new CancelCreateFilter('foo')); expect(result).toEqual(fromFilter.initialState); }); }); diff --git a/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts b/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts index 4b18f91f4..32af51486 100644 --- a/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts +++ b/ui/src/app/metadata/filter/reducer/search.reducer.spec.ts @@ -71,7 +71,10 @@ describe('Filter Reducer', () => { id: 'foo', changes: new EntityAttributesFilterEntity({ resourceId: 'foo', name: 'bar', createdDate: new Date().toLocaleDateString() }), }; - const action = new UpdateFilterSuccess(update); + const action = new UpdateFilterSuccess({ + update, + providerId: 'foo' + }); const result = reducer(snapshot, action); expect(result).toEqual(snapshot); @@ -83,7 +86,10 @@ describe('Filter Reducer', () => { const filter = new EntityAttributesFilterEntity( { resourceId: 'foo', name: 'bar', createdDate: new Date().toLocaleDateString() } ); - const action = new AddFilterSuccess(filter); + const action = new AddFilterSuccess({ + filter, + providerId: 'foo' + }); const result = reducer(snapshot, action); expect(result).toEqual(snapshot); diff --git a/ui/src/testing/activated-route.stub.ts b/ui/src/testing/activated-route.stub.ts index 7e222b6ce..4f7a58f5b 100644 --- a/ui/src/testing/activated-route.stub.ts +++ b/ui/src/testing/activated-route.stub.ts @@ -14,6 +14,7 @@ export class ActivatedRouteStub { // Test parameters private _testParamMap: ParamMap; + private _params: any; private _firstChild: ActivatedRouteStub; @@ -22,12 +23,14 @@ export class ActivatedRouteStub { get testParamMap() { return this._testParamMap; } set testParamMap(params: {}) { this._testParamMap = convertToParamMap(params); + this._params = params; this.subject.next(this._testParamMap); } // ActivatedRoute.snapshot.paramMap get snapshot() { return { + params: this._params, paramMap: this.testParamMap, }; }