From 6ab5c0d85a2b81cbf99fc187a658ce22a1834c49 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 4 Oct 2018 14:19:12 -0700 Subject: [PATCH] SHIBUI-914 Implemented dynamic wizard for sources --- .../model/wizards/metadata-source-wizard.ts | 10 +++ .../container/provider-wizard.component.ts | 3 + .../app/metadata/provider/provider.module.ts | 2 - .../resolver-wizard-step.component.html | 8 ++ .../resolver-wizard-step.component.ts | 89 +++++++++++++++++++ .../container/resolver-wizard.component.html | 46 ++-------- .../container/resolver-wizard.component.ts | 16 +++- .../app/metadata/resolver/resolver.module.ts | 12 ++- .../app/metadata/resolver/resolver.routing.ts | 11 ++- ui/src/app/wizard/reducer/index.spec.ts | 4 +- ui/src/app/wizard/reducer/index.ts | 20 ++++- 11 files changed, 168 insertions(+), 53 deletions(-) create mode 100644 ui/src/app/metadata/resolver/container/resolver-wizard-step.component.html create mode 100644 ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts diff --git a/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts b/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts index 90fcb9d0c..7624065f2 100644 --- a/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts +++ b/ui/src/app/metadata/domain/model/wizards/metadata-source-wizard.ts @@ -5,6 +5,16 @@ export class MetadataSourceWizard implements Wizard { label = 'Metadata Source'; type = '@MetadataProvider'; steps: WizardStep[] = [ + { + index: 1, + id: 'common', + label: 'label.resolver-common-attributes', + schema: 'assets/schema/source/metadata-source.json', + fields: [ + 'serviceProviderName', + 'entityId' + ] + }, { index: 2, id: 'org-info', diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.ts b/ui/src/app/metadata/provider/container/provider-wizard.component.ts index 24140a0a2..73d092b3e 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.ts @@ -38,6 +38,9 @@ export class ProviderWizardComponent implements OnDestroy { constructor( private store: Store ) { + + + this.store .select(fromWizard.getCurrentWizardSchema) .subscribe(s => { diff --git a/ui/src/app/metadata/provider/provider.module.ts b/ui/src/app/metadata/provider/provider.module.ts index 4ce52c7db..550b5c198 100644 --- a/ui/src/app/metadata/provider/provider.module.ts +++ b/ui/src/app/metadata/provider/provider.module.ts @@ -30,8 +30,6 @@ import { UnsavedProviderComponent } from './component/unsaved-provider.dialog'; import { ContentionModule } from '../../contention/contention.module'; import { DeleteFilterComponent } from './component/delete-filter.component'; import { I18nModule } from '../../i18n/i18n.module'; -import { WidgetRegistry } from 'ngx-schema-form'; -import { CustomWidgetRegistry } from '../../schema-form/registry'; @NgModule({ declarations: [ diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.html b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.html new file mode 100644 index 000000000..5d07730fd --- /dev/null +++ b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.html @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts new file mode 100644 index 000000000..5cd58b332 --- /dev/null +++ b/ui/src/app/metadata/resolver/container/resolver-wizard-step.component.ts @@ -0,0 +1,89 @@ +import { Component, OnDestroy } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { withLatestFrom, map, distinctUntilChanged, skipWhile } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; + +import * as fromResolver from '../reducer'; +import * as fromWizard from '../../../wizard/reducer'; + +import { SetDefinition } from '../../../wizard/action/wizard.action'; +import { UpdateStatus, UpdateChanges } from '../action/entity.action'; +import { Wizard } from '../../../wizard/model'; +import { MetadataResolver } from '../../domain/model'; +import { pick } from '../../../shared/util'; + +@Component({ + selector: 'resolver-wizard-step', + templateUrl: './resolver-wizard-step.component.html', + styleUrls: [] +}) + +export class ResolverWizardStepComponent implements OnDestroy { + valueChangeSubject = new Subject>(); + private valueChangeEmitted$ = this.valueChangeSubject.asObservable(); + + statusChangeSubject = new Subject>(); + private statusChangeEmitted$ = this.statusChangeSubject.asObservable(); + + schema$: Observable; + schema: any; + definition$: Observable>; + changes$: Observable; + currentPage: string; + valid$: Observable; + model$: Observable; + + namesList: string[] = []; + + validators$: Observable<{ [key: string]: any }>; + + constructor( + private store: Store, + ) { + this.schema$ = this.store.select(fromWizard.getSchema); + this.definition$ = this.store.select(fromWizard.getWizardDefinition); + this.changes$ = this.store.select(fromResolver.getEntityChanges); + + this.validators$ = this.definition$.pipe( + map((def) => def.getValidators()) + ); + + this.model$ = this.schema$.pipe( + withLatestFrom( + this.store.select(fromWizard.getModel), + this.changes$, + this.definition$ + ), + map(([schema, model, changes, definition]) => ({ + model: { + ...model, + ...changes + }, + definition + })), + skipWhile(({ model, definition }) => !definition || !model), + map(({ model, definition }) => definition.formatter(model)) + ); + + this.valueChangeEmitted$.pipe( + withLatestFrom(this.definition$), + skipWhile(([ changes, definition ]) => !definition || !changes), + map(([ changes, definition ]) => definition.parser(changes)) + ) + .subscribe(changes => this.store.dispatch(new UpdateChanges(changes))); + + this.statusChangeEmitted$.pipe(distinctUntilChanged()).subscribe(errors => this.updateStatus(errors)); + + 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() { + this.valueChangeSubject.complete(); + } +} + diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard.component.html b/ui/src/app/metadata/resolver/container/resolver-wizard.component.html index b8a398c95..1e043a147 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.html +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.html @@ -1,42 +1,10 @@ -
-
-
-
-
- - - Add a new metadata source - – {{ (currentPage$ | async).label | translate }} ({{ (resolver$ | async).serviceProviderName }}) - - -
-
-
-
- + +
+
+ +
diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts index 92b5738f6..3b82f735d 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts @@ -74,9 +74,16 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat }); this.store.dispatch(new SetDefinition(this.sourceWizard)); - this.store.dispatch(new SetIndex(this.sourceWizard.steps[0].id)); - this.store.select(fromWizard.getParsedSchema).subscribe(s => console.log(s)); + this.route.params.subscribe(params => { + if (params.index) { + this.store.dispatch(new SetIndex(params.index)); + } else { + this.store.dispatch(new SetIndex(this.sourceWizard.steps[0].id)); + } + }); + + console.log('hi'); } save(): void { @@ -105,6 +112,8 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat currentState: RouterStateSnapshot, nextState: RouterStateSnapshot ): Observable { + return of(true); + /* if (nextState.url.match('wizard')) { return of(true); } if (Object.keys(this.changes).length > 0) { let modal = this.modalService.open(UnsavedDialogComponent); @@ -115,5 +124,6 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat ); } return this.store.select(fromResolver.getEntityIsSaved); + */ } -} /* istanbul ignore next */ +} diff --git a/ui/src/app/metadata/resolver/resolver.module.ts b/ui/src/app/metadata/resolver/resolver.module.ts index 356fae5b1..468c02e46 100644 --- a/ui/src/app/metadata/resolver/resolver.module.ts +++ b/ui/src/app/metadata/resolver/resolver.module.ts @@ -33,6 +33,9 @@ import { I18nModule } from '../../i18n/i18n.module'; import { MetadataSourceWizard } from '../domain/model/wizards/metadata-source-wizard'; import { METADATA_SOURCE_WIZARD } from './wizard-definition'; import { EntityEffects } from './effect/entity.effect'; +import { ResolverWizardStepComponent } from './container/resolver-wizard-step.component'; +import { WizardModule } from '../../wizard/wizard.module'; +import { FormModule } from '../../schema-form/schema-form.module'; @NgModule({ declarations: [ @@ -44,9 +47,10 @@ import { EntityEffects } from './effect/entity.effect'; ResolverComponent, DraftComponent, EditorComponent, - ResolverWizardComponent, WizardNavComponent, - UnsavedDialogComponent + UnsavedDialogComponent, + ResolverWizardComponent, + ResolverWizardStepComponent ], entryComponents: [ UnsavedDialogComponent @@ -61,7 +65,9 @@ import { EntityEffects } from './effect/entity.effect'; FormsModule, ProviderEditorFormModule, NgbDropdownModule, - I18nModule + I18nModule, + WizardModule, + FormModule ], exports: [ ProviderEditorFormModule, diff --git a/ui/src/app/metadata/resolver/resolver.routing.ts b/ui/src/app/metadata/resolver/resolver.routing.ts index 3e8cc3b47..ca1911706 100644 --- a/ui/src/app/metadata/resolver/resolver.routing.ts +++ b/ui/src/app/metadata/resolver/resolver.routing.ts @@ -13,6 +13,7 @@ import { ConfirmCopyComponent } from './container/confirm-copy.component'; import { CopyIsSetGuard } from './guard/copy-isset.guard'; import { CanDeactivateGuard } from '../../core/service/can-deactivate.guard'; +import { ResolverWizardStepComponent } from './container/resolver-wizard-step.component'; export const ResolverRoutes: Routes = [ { @@ -25,8 +26,14 @@ export const ResolverRoutes: Routes = [ { path: '', redirectTo: 'blank', pathMatch: 'prefix' }, { path: 'blank', - component: BlankResolverComponent, - canDeactivate: [] + component: ResolverWizardComponent, + canDeactivate: [], + children: [ + { + path: '', + component: ResolverWizardStepComponent + } + ] }, { path: 'upload', diff --git a/ui/src/app/wizard/reducer/index.spec.ts b/ui/src/app/wizard/reducer/index.spec.ts index 4bf95f7ae..ec2a2a848 100644 --- a/ui/src/app/wizard/reducer/index.spec.ts +++ b/ui/src/app/wizard/reducer/index.spec.ts @@ -5,12 +5,12 @@ describe('wizard index selectors', () => { describe('getSchema method', () => { it('should return the schema by index name', () => { expect( - selectors.getSchema('common', FileBackedHttpMetadataProviderWizard) + selectors.getSchemaPath('common', FileBackedHttpMetadataProviderWizard) ).toBe(FileBackedHttpMetadataProviderWizard.steps[0].schema); }); it('should return nothing if no schema is found', () => { expect( - selectors.getSchema('common', null) + selectors.getSchemaPath('common', null) ).toBeFalsy(); }); }); diff --git a/ui/src/app/wizard/reducer/index.ts b/ui/src/app/wizard/reducer/index.ts index e2c15147b..8787cad71 100644 --- a/ui/src/app/wizard/reducer/index.ts +++ b/ui/src/app/wizard/reducer/index.ts @@ -46,13 +46,27 @@ export const getWizardIsDisabled = createSelector(getState, fromWizard.getDisabl export const getWizardDefinition = createSelector(getState, fromWizard.getDefinition); export const getSchemaCollection = createSelector(getState, fromWizard.getCollection); -export const getSchema = (index: string, wizard: Wizard) => { +export const getSchemaPath = (index: string, wizard: Wizard) => { if (!wizard) { return null; } const step = wizard.steps.find(s => s.id === index); return step ? step.schema : null; }; -export const getCurrentWizardSchema = createSelector(getWizardIndex, getWizardDefinition, getSchema); +export const getSplitSchema = (schema: any, step: WizardStep) => { + if (!schema || !step.fields || !step.fields.length) { + return schema; + } + const keys = Object.keys(schema.properties).filter(key => step.fields.indexOf(key) > -1); + + return { + ...schema, + properties: { + ...keys.reduce( (properties, key) => ({ ...properties, [key]: schema.properties[key] }) , {}) + } + }; +}; + +export const getCurrentWizardSchema = createSelector(getWizardIndex, getWizardDefinition, getSchemaPath); export const getPreviousFn = (index: string, wizard: Wizard) => { if (!wizard) { return null; } @@ -96,3 +110,5 @@ export const getLocked = createSelector(getCurrent, getLockedStatus, getSchemaLo export const getSchemaObject = createSelector(getState, fromWizard.getSchema); export const getParsedSchema = createSelector(getSchemaObject, getLocked, getSchemaParseFn); + +export const getSchema = createSelector(getParsedSchema, getCurrent, getSplitSchema);