From 1166ba4edf048ef68cd298531e942e6407697d2e Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 26 Sep 2019 13:02:36 -0700 Subject: [PATCH 01/12] SHIBUI-1437 Integrated config component to replace summary --- .../configuration/configuration.module.ts | 7 +- .../metadata/configuration/reducer/index.ts | 83 +------------- .../configuration/reducer/utilities.ts | 84 ++++++++++++++ .../service/configuration.service.ts | 2 +- .../component/wizard-summary.component.html | 13 --- .../wizard-summary.component.spec.ts | 103 ------------------ .../component/wizard-summary.component.ts | 81 -------------- ui/src/app/metadata/domain/domain.module.ts | 2 - .../container/provider-wizard.component.html | 6 +- .../container/resolver-wizard.component.html | 10 +- .../container/resolver-wizard.component.ts | 18 +-- ui/src/app/metadata/resolver/reducer/index.ts | 9 ++ .../app/metadata/resolver/resolver.module.ts | 4 +- 13 files changed, 112 insertions(+), 310 deletions(-) create mode 100644 ui/src/app/metadata/configuration/reducer/utilities.ts delete mode 100644 ui/src/app/metadata/domain/component/wizard-summary.component.html delete mode 100644 ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts delete mode 100644 ui/src/app/metadata/domain/component/wizard-summary.component.ts diff --git a/ui/src/app/metadata/configuration/configuration.module.ts b/ui/src/app/metadata/configuration/configuration.module.ts index 338c39ccb..6430886b8 100644 --- a/ui/src/app/metadata/configuration/configuration.module.ts +++ b/ui/src/app/metadata/configuration/configuration.module.ts @@ -84,7 +84,9 @@ import { FilterCompareVersionEffects } from './effect/filter.effect'; WizardModule, FormModule ], - exports: [], + exports: [ + MetadataConfigurationComponent + ], providers: [ DatePipe, IndexResolver @@ -114,7 +116,8 @@ export class MetadataConfigurationModule { RestoreEffects, FilterCompareVersionEffects, VersionEffects - ]) + ] + ) ], providers: [] }) diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index 13daf9b34..3b08a9367 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -10,6 +10,7 @@ import * as fromFilter from './filter.reducer'; import { WizardStep } from '../../../wizard/model'; import * as utils from '../../domain/utility/configuration'; +import { getConfigurationSectionsFn, assignValueToProperties } from './utilities'; import { getSplitSchema, getModel } from '../../../wizard/reducer'; import { getInCollectionFn } from '../../domain/domain.util'; import { MetadataConfiguration } from '../model/metadata-configuration'; @@ -58,88 +59,6 @@ export const getConfigurationDefinition = createSelector(getConfigurationState, export const getConfigurationSchema = createSelector(getConfigurationState, fromConfiguration.getSchema); export const getConfigurationXml = createSelector(getConfigurationState, fromConfiguration.getXml); -export const assignValueToProperties = (models, properties, definition: any): any[] => { - return properties.map(prop => { - const differences = models.some((model, index, array) => { - if (!array) { - return false; - } - return JSON.stringify(model[prop.id]) !== JSON.stringify(array[0][prop.id]); - }); - - const widget = prop.type === 'array' && prop.widget && prop.widget.data ? ({ - ...prop.widget, - data: prop.widget.data.map(item => ({ - ...item, - differences: models - .map((model) => { - const value = model[prop.id]; - return value ? value.indexOf(item.key) > -1 : false; - }) - .reduce((current, val) => current !== val ? true : false, false) - })) - }) : null; - - switch (prop.type) { - case 'object': - return { - ...prop, - differences, - properties: assignValueToProperties( - models.map(model => definition.formatter(model)[prop.id] || {}), - prop.properties, - definition - ) - }; - default: - return { - ...prop, - differences, - value: models.map(model => { - return model[prop.id]; - }), - widget - }; - } - }); -}; - -export const getConfigurationSectionsFn = (models, definition, schema): MetadataConfiguration => { - return !definition || !schema || !models ? null : - ({ - dates: models.map(m => m ? m.modifiedDate : null), - sections: definition.steps - .filter(step => step.id !== 'summary') - .map( - (step: WizardStep, num: number) => { - return ({ - id: step.id, - pageNumber: num + 1, - index: step.index, - label: step.label, - properties: utils.getStepProperties( - getSplitSchema(schema, step), - definition.formatter({}), - schema.definitions || {} - ) - }); - } - ) - .map((section: any) => { - return { - ...section, - properties: assignValueToProperties(models, section.properties, definition) - }; - }) - .map((section: any) => ({ - ...section, - differences: section.properties.some(prop => prop.differences) - })) - }); - }; - - - export const getConfigurationModelEnabledFn = (config: Metadata) => config ? ('serviceEnabled' in config) ? config.serviceEnabled : config.enabled : false; diff --git a/ui/src/app/metadata/configuration/reducer/utilities.ts b/ui/src/app/metadata/configuration/reducer/utilities.ts new file mode 100644 index 000000000..16668a027 --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/utilities.ts @@ -0,0 +1,84 @@ +import { MetadataConfiguration } from '../model/metadata-configuration'; +import { WizardStep } from '../../../wizard/model'; +import * as utils from '../../domain/utility/configuration'; +import { getSplitSchema } from '../../../wizard/reducer'; + +export const getConfigurationSectionsFn = (models, definition, schema): MetadataConfiguration => { + return !definition || !schema || !models ? null : + ({ + dates: models.map(m => m ? m.modifiedDate : null), + sections: definition.steps + .filter(step => step.id !== 'summary') + .map( + (step: WizardStep, num: number) => { + return ({ + id: step.id, + pageNumber: num + 1, + index: step.index, + label: step.label, + properties: utils.getStepProperties( + getSplitSchema(schema, step), + definition.formatter({}), + schema.definitions || {} + ) + }); + } + ) + .map((section: any) => { + return { + ...section, + properties: assignValueToProperties(models, section.properties, definition) + }; + }) + .map((section: any) => ({ + ...section, + differences: section.properties.some(prop => prop.differences) + })) + }); +}; + +export const assignValueToProperties = (models, properties, definition: any): any[] => { + return properties.map(prop => { + const differences = models.some((model, index, array) => { + if (!array) { + return false; + } + return JSON.stringify(model[prop.id]) !== JSON.stringify(array[0][prop.id]); + }); + + const widget = prop.type === 'array' && prop.widget && prop.widget.data ? ({ + ...prop.widget, + data: prop.widget.data.map(item => ({ + ...item, + differences: models + .map((model) => { + const value = model[prop.id]; + return value ? value.indexOf(item.key) > -1 : false; + }) + .reduce((current, val) => current !== val ? true : false, false) + })) + }) : null; + + switch (prop.type) { + case 'object': + return { + ...prop, + differences, + properties: assignValueToProperties( + models.map(model => definition.formatter(model)[prop.id] || {}), + prop.properties, + definition + ) + }; + default: + return { + ...prop, + differences, + value: models.map(model => { + return model[prop.id]; + }), + widget + }; + } + }); +}; diff --git a/ui/src/app/metadata/configuration/service/configuration.service.ts b/ui/src/app/metadata/configuration/service/configuration.service.ts index ef03fc2e5..df8a3dba0 100644 --- a/ui/src/app/metadata/configuration/service/configuration.service.ts +++ b/ui/src/app/metadata/configuration/service/configuration.service.ts @@ -10,7 +10,7 @@ import { TYPES } from '../configuration.values'; import { ResolverService } from '../../domain/service/resolver.service'; import { MetadataProviderService } from '../../domain/service/provider.service'; import { MetadataFilterEditorTypes } from '../../filter/model'; -import { getConfigurationSectionsFn } from '../reducer'; +import { getConfigurationSectionsFn } from '../reducer/utilities'; @Injectable() export class MetadataConfigurationService { diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.html b/ui/src/app/metadata/domain/component/wizard-summary.component.html deleted file mode 100644 index e7ea5dbf6..000000000 --- a/ui/src/app/metadata/domain/component/wizard-summary.component.html +++ /dev/null @@ -1,13 +0,0 @@ -
-
-
- - - - -
-
-
diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts b/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts deleted file mode 100644 index ecd51b1b9..000000000 --- a/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Component, ViewChild } from '@angular/core'; -import { TestBed, async, ComponentFixture } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; - -import { NgbDropdownModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; - -import { WizardSummaryComponent } from './wizard-summary.component'; -import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-schema-form'; -import { Wizard } from '../../../wizard/model'; -import { MetadataProvider } from '../../domain/model'; -import { SummaryPropertyComponent } from './summary-property.component'; -import { SCHEMA } from '../../../../testing/form-schema.stub'; -import { MockI18nModule } from '../../../../testing/i18n.stub'; -import { MetadataProviderWizard } from '../../provider/model'; -import { AttributesService } from '../service/attributes.service'; -import { MockAttributeService } from '../../../../testing/attributes.stub'; - -@Component({ - template: ` - - ` -}) -class TestHostComponent { - @ViewChild(WizardSummaryComponent) - public componentUnderTest: WizardSummaryComponent; - - private _summary; - - get summary(): { definition: Wizard, schema: { [id: string]: any }, model: any } { - return this._summary; - } - - set summary(summary: { definition: Wizard, schema: { [id: string]: any }, model: any }) { - this._summary = summary; - } -} - -describe('Provider Wizard Summary Component', () => { - - let fixture: ComponentFixture; - let instance: TestHostComponent; - let app: WizardSummaryComponent; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - NgbDropdownModule, - NgbPopoverModule, - RouterTestingModule, - SchemaFormModule.forRoot(), - MockI18nModule - ], - declarations: [ - WizardSummaryComponent, - SummaryPropertyComponent, - TestHostComponent - ], - providers: [ - { provide: WidgetRegistry, useClass: DefaultWidgetRegistry }, - { provide: AttributesService, useClass: MockAttributeService } - ] - }).compileComponents(); - - fixture = TestBed.createComponent(TestHostComponent); - instance = fixture.componentInstance; - app = instance.componentUnderTest; - fixture.detectChanges(); - })); - - it('should instantiate the component', async(() => { - expect(app).toBeTruthy(); - })); - - describe('gotoPage function', () => { - it('should emit an empty string if page is null', () => { - spyOn(app.onPageSelect, 'emit'); - app.gotoPage(); - expect(app.onPageSelect.emit).toHaveBeenCalledWith(''); - }); - - it('should emit the provided page', () => { - spyOn(app.onPageSelect, 'emit'); - app.gotoPage('foo'); - expect(app.onPageSelect.emit).toHaveBeenCalledWith('foo'); - }); - }); - - describe('ngOnChanges', () => { - it('should set columns and sections if summary is provided', () => { - instance.summary = { - model: { - name: 'foo', - '@type': 'MetadataProvider' - }, - schema: SCHEMA, - definition: MetadataProviderWizard - }; - fixture.detectChanges(); - expect(app.sections).toBeDefined(); - expect(app.columns).toBeDefined(); - }); - }); -}); diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.ts b/ui/src/app/metadata/domain/component/wizard-summary.component.ts deleted file mode 100644 index 80f246c83..000000000 --- a/ui/src/app/metadata/domain/component/wizard-summary.component.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Component, Input, SimpleChanges, OnChanges, Output, EventEmitter } from '@angular/core'; -import merge from 'deepmerge'; - -import { Wizard, WizardStep } from '../../../wizard/model'; -import { MetadataProvider, MetadataResolver } from '../../domain/model'; -import { Property } from '../model/property'; -import { getSplitSchema } from '../../../wizard/reducer'; -import { getStepProperties } from '../utility/configuration'; - -interface Section { - id: string; - index: number; - label: string; - pageNumber: number; - properties: Property[]; -} - -@Component({ - selector: 'wizard-summary', - templateUrl: './wizard-summary.component.html', - styleUrls: [] -}) - -export class WizardSummaryComponent implements OnChanges { - @Input() summary: { definition: Wizard, schema: { [id: string]: any }, model: any }; - - @Output() onPageSelect: EventEmitter = new EventEmitter(); - - sections: Section[]; - columns: Array
[]; - steps: WizardStep[]; - - constructor() {} - - ngOnChanges(changes: SimpleChanges): void { - if (changes.summary && this.summary) { - const schema = this.summary.schema; - const model = this.summary.model; - const def = this.summary.definition; - const steps = def.steps; - - this.sections = steps - .filter(step => step.id !== 'summary') - .map( - (step: WizardStep, num: number) => { - const { id, index, label } = step; - const split = getSplitSchema(schema, step); - const properties = getStepProperties( - split, - def.formatter(model), - schema.definitions || {} - ); - return ({ - id, - pageNumber: num + 1, - index, - label, - properties - }); - } - ); - - this.columns = this.sections.reduce((resultArray, item, index) => { - const chunkIndex = Math.floor(index / Math.round(this.sections.length / 2)); - - if (!resultArray[chunkIndex]) { - resultArray[chunkIndex] = []; - } - - resultArray[chunkIndex].push(item); - - return resultArray; - }, []); - } - } - - gotoPage(page: string = ''): void { - this.onPageSelect.emit(page); - } -} - diff --git a/ui/src/app/metadata/domain/domain.module.ts b/ui/src/app/metadata/domain/domain.module.ts index ffee8cf1d..b109734a6 100644 --- a/ui/src/app/metadata/domain/domain.module.ts +++ b/ui/src/app/metadata/domain/domain.module.ts @@ -16,7 +16,6 @@ import { PreviewDialogComponent } from './component/preview-dialog.component'; import { MetadataFilterService } from './service/filter.service'; import { AttributesService } from './service/attributes.service'; import { I18nModule } from '../../i18n/i18n.module'; -import { WizardSummaryComponent } from './component/wizard-summary.component'; import { SummaryPropertyComponent } from './component/summary-property.component'; import { UnsavedEntityComponent } from './component/unsaved-entity.dialog'; @@ -26,7 +25,6 @@ import { SharedModule } from '../../shared/shared.module'; export const COMPONENTS = [ PreviewDialogComponent, - WizardSummaryComponent, UnsavedEntityComponent, SummaryPropertyComponent, EditorNavComponent diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.html b/ui/src/app/metadata/provider/container/provider-wizard.component.html index 366bb70d5..58766e536 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.html +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.html @@ -19,11 +19,7 @@ - - +
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 87100a871..867ac0584 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.html +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.html @@ -5,8 +5,8 @@ - - + + + 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 56848ad5e..7a3bfc781 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts @@ -29,6 +29,8 @@ import { LoadSchemaRequest } from '../../../wizard/action/wizard.action'; import { UnsavedEntityComponent } from '../../domain/component/unsaved-entity.dialog'; import { Clear } from '../action/entity.action'; import { DifferentialService } from '../../../core/service/differential.service'; +import { getConfigurationSections } from '../../configuration/reducer'; +import { MetadataConfiguration } from '../../configuration/model/metadata-configuration'; @Component({ selector: 'resolver-wizard-page', @@ -60,7 +62,7 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat valid$: Observable; schema$: Observable; - summary$: Observable<{ definition: Wizard, schema: { [id: string]: any }, model: any }>; + summary$: Observable = this.store.select(fromCollections.getResolverConfiguration); constructor( private store: Store, @@ -115,20 +117,6 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat combineLatest(this.resolver$, (changes, base) => ({ ...base, ...changes })) ).subscribe(latest => this.latest = latest); - this.summary$ = combine( - this.store.select(fromWizard.getWizardDefinition), - this.store.select(fromWizard.getSchemaObject), - this.store.select(fromResolver.getDraftModelWithChanges) - ).pipe( - map(([definition, schema, model]) => ( - { - definition, - schema: schema || {}, - model - } - )) - ); - this.changes$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(c => this.changes = c); this.resolver$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(r => this.resolver = r); } diff --git a/ui/src/app/metadata/resolver/reducer/index.ts b/ui/src/app/metadata/resolver/reducer/index.ts index a1cc5256d..e25643a25 100644 --- a/ui/src/app/metadata/resolver/reducer/index.ts +++ b/ui/src/app/metadata/resolver/reducer/index.ts @@ -8,6 +8,7 @@ import * as fromCollection from './collection.reducer'; import * as fromWizard from '../../../wizard/reducer'; import { combineAllFn, getEntityIdsFn, getInCollectionFn, doesExistFn } from '../../domain/domain.util'; +import { getConfigurationSectionsFn } from '../../configuration/reducer/utilities'; export interface ResolverState { entity: fromEntity.EntityState; @@ -124,3 +125,11 @@ export const getDraftModelWithChanges = createSelector( }) ); +export const getDraftModelList = createSelector(getDraftModelWithChanges, (model) => [model]); + +export const getResolverConfiguration = createSelector( + getDraftModelList, + fromWizard.getWizardDefinition, + fromWizard.getSchema, + getConfigurationSectionsFn +); diff --git a/ui/src/app/metadata/resolver/resolver.module.ts b/ui/src/app/metadata/resolver/resolver.module.ts index e0cb4bb5b..e303181fc 100644 --- a/ui/src/app/metadata/resolver/resolver.module.ts +++ b/ui/src/app/metadata/resolver/resolver.module.ts @@ -36,6 +36,7 @@ import { MetadataSourceEditor } from '../domain/model/wizards/metadata-source-ed import { FinishFormComponent } from './component/finish-form.component'; import { ProviderFormFragmentComponent } from './component/provider-form-fragment.component'; import { MetadataResolverPageComponent } from './resolver.component'; +import { MetadataConfigurationModule } from '../configuration/configuration.module'; @NgModule({ declarations: [ @@ -67,7 +68,8 @@ import { MetadataResolverPageComponent } from './resolver.component'; WizardModule, FormModule, NgbPopoverModule, - NgbModalModule + NgbModalModule, + MetadataConfigurationModule ], exports: [], providers: [] From e9ef6e26d284b8c2924479411a74d925ce73e062 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 26 Sep 2019 13:47:27 -0700 Subject: [PATCH 02/12] SHIBUI-1508 Fixed issues with configuration page --- .../component/metadata-configuration.component.ts | 2 +- ui/src/app/metadata/configuration/reducer/index.ts | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts index e2d75c4da..5bd277fbb 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts @@ -28,7 +28,7 @@ export class MetadataConfigurationComponent implements OnChanges { ) {} ngOnChanges(): void { - this.zero = this.configuration.sections.some(s => !s.properties.length); + this.zero = !this.configuration.sections.some(s => !!s.properties.length); } edit(id: string): void { diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index 8deb962ec..26391abd0 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -104,9 +104,7 @@ export const assignValueToProperties = (models, properties, definition: any): an return { ...prop, differences, - value: models.map(model => { - return model[prop.id]; - }), + value: models.map(model => model[prop.id]), widget }; } @@ -153,7 +151,7 @@ export const getConfigurationModelEnabledFn = (config: Metadata) => config ? ('serviceEnabled' in config) ? config.serviceEnabled : config.enabled : false; export const getConfigurationModelNameFn = - (config: Metadata) => config ? ('serviceProviderName' in config) ? config.serviceProviderName : config.name : false; + (config: Metadata) => config ? ('serviceProviderName' in config) ? config.serviceProviderName : config.name : ''; export const getConfigurationModelTypeFn = (config: Metadata) => config ? ('@type' in config) ? config['@type'] : 'resolver' : null; @@ -163,8 +161,8 @@ export const isAdditionalFilter = (type) => filterPluginTypes.indexOf(type) === export const getVersionModelFiltersFn = (model, kind) => kind === 'provider' ? - model.metadataFilters.filter(filter => isAdditionalFilter(filter['@type'])) : - null; + model.metadataFilters ? model.metadataFilters.filter(filter => isAdditionalFilter(filter['@type'])) : + [] : null; // Version History From 45711c5daf82e21326b5d497e4b432063826d596 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 27 Sep 2019 11:41:49 -0700 Subject: [PATCH 03/12] SHIBUI-1437 Implemented updated view for summary --- ...ter-configuration-list-item.component.html | 1 - .../metadata-configuration.component.html | 3 +-- .../metadata-configuration.component.ts | 20 +++++++++---------- .../metadata-comparison.component.html | 7 ++----- .../container/metadata-options.component.html | 6 +++++- .../container/metadata-options.component.ts | 10 ++++++++-- .../container/version-options.component.html | 1 - .../metadata/configuration/reducer/index.ts | 6 ++---- .../container/resolver-wizard.component.html | 5 +++-- .../container/resolver-wizard.component.ts | 17 +++++++--------- ui/src/app/metadata/resolver/reducer/index.ts | 2 +- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.html b/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.html index cf1cfe3be..6a447d6f3 100644 --- a/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.html +++ b/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.html @@ -35,7 +35,6 @@ {{ section.label | translate }} -
+
- + [configuration]="versions$ | async">

Metadata Filter @@ -41,8 +39,7 @@


+ [configuration]="filterCompare$ | async">

- + +
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 8b3d3281f..484d03bc5 100644 --- a/ui/src/app/metadata/configuration/container/metadata-options.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.ts @@ -2,7 +2,7 @@ import { Store } from '@ngrx/store'; import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; import { ViewportScroller } from '@angular/common'; import { takeUntil, filter } from 'rxjs/operators'; @@ -53,7 +53,9 @@ export class MetadataOptionsComponent implements OnDestroy { constructor( protected store: Store, protected modalService: NgbModal, - protected scroller: ViewportScroller + protected scroller: ViewportScroller, + protected router: Router, + protected activatedRoute: ActivatedRoute ) { this.model$ .pipe( @@ -63,6 +65,10 @@ export class MetadataOptionsComponent implements OnDestroy { .subscribe(p => this.setModel(p)); } + edit(id: string) { + this.router.navigate(['../', 'edit', id], { relativeTo: this.activatedRoute.parent }); + } + setModel(data: Metadata): void { this.id = 'resourceId' in data ? data.resourceId : data.id; this.kind = '@type' in data ? 'provider' : 'resolver'; diff --git a/ui/src/app/metadata/configuration/container/version-options.component.html b/ui/src/app/metadata/configuration/container/version-options.component.html index c4590848f..f18e661bf 100644 --- a/ui/src/app/metadata/configuration/container/version-options.component.html +++ b/ui/src/app/metadata/configuration/container/version-options.component.html @@ -23,7 +23,6 @@

diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index 4ffb7e081..1053ada29 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -7,13 +7,11 @@ import * as fromCompare from './compare.reducer'; import * as fromVersion from './version.reducer'; import * as fromRestore from './restore.reducer'; import * as fromFilter from './filter.reducer'; -import { WizardStep } from '../../../wizard/model'; import * as utils from '../../domain/utility/configuration'; -import { getConfigurationSectionsFn, assignValueToProperties } from './utilities'; -import { getSplitSchema, getModel } from '../../../wizard/reducer'; +import { getConfigurationSectionsFn } from './utilities'; +import { getModel } from '../../../wizard/reducer'; import { getInCollectionFn } from '../../domain/domain.util'; -import { MetadataConfiguration } from '../model/metadata-configuration'; import { Metadata } from '../../domain/domain.type'; import * as fromResolver from '../../resolver/reducer'; 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 867ac0584..425a84920 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.html +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.html @@ -5,8 +5,9 @@
- +
- +
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 7a3bfc781..bd7c6e437 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.ts @@ -9,7 +9,7 @@ import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { Observable, Subject, of, combineLatest as combine } from 'rxjs'; +import { Observable, Subject, of } from 'rxjs'; import { skipWhile, startWith, distinctUntilChanged, map, takeUntil, combineLatest, withLatestFrom } from 'rxjs/operators'; import { Store } from '@ngrx/store'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -28,8 +28,6 @@ import * as fromWizard from '../../../wizard/reducer'; import { LoadSchemaRequest } from '../../../wizard/action/wizard.action'; import { UnsavedEntityComponent } from '../../domain/component/unsaved-entity.dialog'; import { Clear } from '../action/entity.action'; -import { DifferentialService } from '../../../core/service/differential.service'; -import { getConfigurationSections } from '../../configuration/reducer'; import { MetadataConfiguration } from '../../configuration/model/metadata-configuration'; @Component({ @@ -69,7 +67,6 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat private route: ActivatedRoute, private router: Router, private modalService: NgbModal, - private diffService: DifferentialService, @Inject(METADATA_SOURCE_WIZARD) private sourceWizard: Wizard ) { this.store @@ -151,16 +148,16 @@ export class ResolverWizardComponent implements OnDestroy, CanComponentDeactivat this.store.dispatch(new SetIndex(page)); } + get blacklist(): string[] { + return ['id', 'resourceId']; + } + hasChanges(changes: MetadataResolver): boolean { - // const updated = this.diffService.updatedDiff(this.resolver, changes); - // const deleted = this.diffService.deletedDiff(this.resolver, changes); - let blacklist = ['id', 'resourceId']; - return Object.keys(changes).filter(key => !(blacklist.indexOf(key) > -1)).length > 0; + return Object.keys(changes).filter(key => !(this.blacklist.indexOf(key) > -1)).length > 0; } isNew(changes: MetadataResolver): boolean { - let blacklist = ['id', 'resourceId']; - return Object.keys(changes).filter(key => !(blacklist.indexOf(key) > -1)).length === 0; + return Object.keys(changes).filter(key => !(this.blacklist.indexOf(key) > -1)).length === 0; } ngOnDestroy(): void { diff --git a/ui/src/app/metadata/resolver/reducer/index.ts b/ui/src/app/metadata/resolver/reducer/index.ts index e25643a25..a22693e4d 100644 --- a/ui/src/app/metadata/resolver/reducer/index.ts +++ b/ui/src/app/metadata/resolver/reducer/index.ts @@ -130,6 +130,6 @@ export const getDraftModelList = createSelector(getDraftModelWithChanges, (model export const getResolverConfiguration = createSelector( getDraftModelList, fromWizard.getWizardDefinition, - fromWizard.getSchema, + fromWizard.getProcessedSchema, getConfigurationSectionsFn ); From 40feb8a1209c12e922341e736a19dadf23562225 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 27 Sep 2019 14:07:02 -0700 Subject: [PATCH 04/12] Updated unit tests, improved coverage --- .../metadata-configuration.component.spec.ts | 6 +- .../configuration/reducer/index.spec.ts | 118 +---------------- .../metadata/configuration/reducer/index.ts | 24 +--- .../configuration/reducer/utilities.spec.ts | 120 ++++++++++++++++++ .../configuration/reducer/utilities.ts | 21 +++ .../service/configuration.service.spec.ts | 2 +- .../component/wizard-summary.component.html | 14 ++ .../wizard-summary.component.spec.ts | 103 +++++++++++++++ .../component/wizard-summary.component.ts | 80 ++++++++++++ ui/src/app/metadata/domain/domain.module.ts | 2 + .../model/dynamic-http.provider.form.spec.ts | 85 +++++++++++++ .../metadata/provider/model/utilities.spec.ts | 33 +++++ .../app/metadata/provider/model/utilities.ts | 2 - .../resolver-wizard.component.spec.ts | 6 +- ui/src/app/wizard/reducer/index.ts | 2 +- 15 files changed, 471 insertions(+), 147 deletions(-) create mode 100644 ui/src/app/metadata/configuration/reducer/utilities.spec.ts create mode 100644 ui/src/app/metadata/domain/component/wizard-summary.component.html create mode 100644 ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts create mode 100644 ui/src/app/metadata/domain/component/wizard-summary.component.ts create mode 100644 ui/src/app/metadata/provider/model/utilities.spec.ts diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.spec.ts b/ui/src/app/metadata/configuration/component/metadata-configuration.component.spec.ts index c17a469af..3feb146b5 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.spec.ts +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.spec.ts @@ -66,10 +66,10 @@ describe('Metadata Configuration Component', () => { })); describe('edit method', () => { - it('should call router.navigate', () => { - spyOn(router, 'navigate'); + it('should call onEdit.emit', () => { + spyOn(app.onEdit, 'emit'); app.edit('foo'); - expect(router.navigate).toHaveBeenCalled(); + expect(app.onEdit.emit).toHaveBeenCalled(); }); }); diff --git a/ui/src/app/metadata/configuration/reducer/index.spec.ts b/ui/src/app/metadata/configuration/reducer/index.spec.ts index 8a1aa3cb7..d9dff5a83 100644 --- a/ui/src/app/metadata/configuration/reducer/index.spec.ts +++ b/ui/src/app/metadata/configuration/reducer/index.spec.ts @@ -1,143 +1,31 @@ import { - getConfigurationSectionsFn, getConfigurationModelNameFn, getConfigurationModelEnabledFn, - assignValueToProperties, - getLimitedPropertiesFn, getConfigurationModelTypeFn, getSelectedVersionNumberFn, getSelectedIsCurrentFn } from './index'; -import { SCHEMA as schema } from '../../../../testing/form-schema.stub'; + import { Metadata } from '../../domain/domain.type'; -import { MockMetadataWizard } from '../../../../testing/mockMetadataWizard'; describe('Configuration Reducer', () => { - const model = { - name: 'foo', - serviceEnabled: true, - foo: { - bar: 'bar', - baz: 'baz' - }, - list: [ - 'super', - 'cool' - ] - }; - - const props = [ - { - id: 'name', - items: null, - name: 'label.metadata-provider-name-dashboard-display-only', - properties: [], - type: 'string', - value: null, - widget: { id: 'string', help: 'message.must-be-unique' } - }, - { - id: 'serviceEnabled', - items: null, - name: 'serviceEnabled', - properties: [], - type: 'string', - value: null, - widget: { id: 'select', disabled: true } - }, - { - id: 'foo', - items: null, - name: 'foo', - type: 'object', - properties: [ - { - id: 'bar', - name: 'bar', - type: 'string', - properties: [] - }, - { - id: 'baz', - name: 'baz', - type: 'string', - properties: [] - } - ] - }, - { - id: 'list', - name: 'list', - type: 'array', - items: { - type: 'string' - }, - widget: { - id: 'datalist', - data: [ - { key: 'super', label: 'super' }, - { key: 'cool', label: 'cool' }, - { key: 'notcool', label: 'notcool' } - ] - } - } - ]; - - const definition = MockMetadataWizard; - - describe('getConfigurationSectionsFn', () => { - it('should parse the schema, definition, and model into a MetadataConfiguration', () => { - const config = getConfigurationSectionsFn([model], definition, schema); - expect(config.sections).toBeDefined(); - }); - }); describe('getConfigurationModelNameFn function', () => { it('should return the name attribute', () => { expect(getConfigurationModelNameFn({ serviceProviderName: 'foo' } as Metadata)).toBe('foo'); expect(getConfigurationModelNameFn({ name: 'bar' } as Metadata)).toBe('bar'); - expect(getConfigurationModelNameFn(null)).toBe(false); + expect(getConfigurationModelNameFn(null)).toBe(''); }); }); describe('getConfigurationModelEnabledFn function', () => { - it('should return the name attribute', () => { + it('should return the enabled attribute', () => { expect(getConfigurationModelEnabledFn({ serviceEnabled: true } as Metadata)).toBe(true); expect(getConfigurationModelEnabledFn({ enabled: true } as Metadata)).toBe(true); expect(getConfigurationModelEnabledFn(null)).toBe(false); }); }); - describe('assignValueToProperties function', () => { - it('should assign appropriate values to the given schema properties', () => { - const assigned = assignValueToProperties([model], props, definition); - expect(assigned[0].value).toEqual(['foo']); - expect(assigned[1].value).toEqual([true]); - }); - - it('should assign differences when passed multiple models', () => { - const assigned = assignValueToProperties([model, { - ...model, - name: 'bar', - list: [ - 'super', - 'notcool' - ] - }], props, definition); - expect(assigned[0].differences).toBe(true); - }); - }); - - describe('getLimitedPropertiesFn function', () => { - it('should filter properties without differences', () => { - const assigned = assignValueToProperties([model, { - ...model, - name: 'bar' - }], props, definition); - expect(getLimitedPropertiesFn(assigned).length).toBe(1); - }); - }); - describe('getConfigurationModelTypeFn function ', () => { it('should return provider type if the object has an @type property', () => { const md = { '@type': 'FilebackedHttpMetadataResolver' } as Metadata; diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index 1053ada29..58e92562f 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -8,15 +8,13 @@ import * as fromVersion from './version.reducer'; import * as fromRestore from './restore.reducer'; import * as fromFilter from './filter.reducer'; -import * as utils from '../../domain/utility/configuration'; -import { getConfigurationSectionsFn } from './utilities'; +import { getConfigurationSectionsFn, getLimitedPropertiesFn } from './utilities'; import { getModel } from '../../../wizard/reducer'; import { getInCollectionFn } from '../../domain/domain.util'; import { Metadata } from '../../domain/domain.type'; import * as fromResolver from '../../resolver/reducer'; import * as fromProvider from '../../provider/reducer'; -import { SectionProperty } from '../model/section'; export interface ConfigurationState { configuration: fromConfiguration.State; @@ -136,26 +134,6 @@ export const getComparisonConfigurationCount = createSelector(getComparisonConfi export const getViewChangedOnly = createSelector(getCompareState, fromCompare.getViewChangedOnly); -export const getLimitedPropertiesFn = (properties: SectionProperty[]) => { - return ([ - ...properties - .filter(p => p.differences) - .map(p => { - const parsed = { ...p }; - if (p.widget && p.widget.data) { - parsed.widget = { - ...p.widget, - data: p.widget.data.filter(item => item.differences) - }; - } - if (p.properties) { - parsed.properties = getLimitedPropertiesFn(p.properties); - } - return parsed; - }) - ]); -}; - export const getLimitedConfigurationsFn = (configurations, limited) => configurations ? ({ ...configurations, sections: limited ? configurations.sections : diff --git a/ui/src/app/metadata/configuration/reducer/utilities.spec.ts b/ui/src/app/metadata/configuration/reducer/utilities.spec.ts new file mode 100644 index 000000000..0d8234bb5 --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/utilities.spec.ts @@ -0,0 +1,120 @@ +import { + getConfigurationSectionsFn, + getLimitedPropertiesFn, + assignValueToProperties +} from './utilities'; + +import { SCHEMA as schema } from '../../../../testing/form-schema.stub'; +import { MockMetadataWizard } from '../../../../testing/mockMetadataWizard'; + +describe('config reducer utilities', () => { + + const model = { + name: 'foo', + serviceEnabled: true, + foo: { + bar: 'bar', + baz: 'baz' + }, + list: [ + 'super', + 'cool' + ] + }; + + const props = [ + { + id: 'name', + items: null, + name: 'label.metadata-provider-name-dashboard-display-only', + properties: [], + type: 'string', + value: null, + widget: { id: 'string', help: 'message.must-be-unique' } + }, + { + id: 'serviceEnabled', + items: null, + name: 'serviceEnabled', + properties: [], + type: 'string', + value: null, + widget: { id: 'select', disabled: true } + }, + { + id: 'foo', + items: null, + name: 'foo', + type: 'object', + properties: [ + { + id: 'bar', + name: 'bar', + type: 'string', + properties: [] + }, + { + id: 'baz', + name: 'baz', + type: 'string', + properties: [] + } + ] + }, + { + id: 'list', + name: 'list', + type: 'array', + items: { + type: 'string' + }, + widget: { + id: 'datalist', + data: [ + { key: 'super', label: 'super' }, + { key: 'cool', label: 'cool' }, + { key: 'notcool', label: 'notcool' } + ] + } + } + ]; + + const definition = MockMetadataWizard; + + describe('assignValueToProperties function', () => { + it('should assign appropriate values to the given schema properties', () => { + const assigned = assignValueToProperties([model], props, definition); + expect(assigned[0].value).toEqual(['foo']); + expect(assigned[1].value).toEqual([true]); + }); + + it('should assign differences when passed multiple models', () => { + const assigned = assignValueToProperties([model, { + ...model, + name: 'bar', + list: [ + 'super', + 'notcool' + ] + }], props, definition); + expect(assigned[0].differences).toBe(true); + }); + }); + + describe('getLimitedPropertiesFn function', () => { + it('should filter properties without differences', () => { + const assigned = assignValueToProperties([model, { + ...model, + name: 'bar' + }], props, definition); + expect(getLimitedPropertiesFn(assigned).length).toBe(1); + }); + }); + + describe('getConfigurationSectionsFn', () => { + it('should parse the schema, definition, and model into a MetadataConfiguration', () => { + const config = getConfigurationSectionsFn([model], definition, schema); + expect(config.sections).toBeDefined(); + }); + }); +}); diff --git a/ui/src/app/metadata/configuration/reducer/utilities.ts b/ui/src/app/metadata/configuration/reducer/utilities.ts index 16668a027..f2481e99c 100644 --- a/ui/src/app/metadata/configuration/reducer/utilities.ts +++ b/ui/src/app/metadata/configuration/reducer/utilities.ts @@ -2,6 +2,7 @@ import { MetadataConfiguration } from '../model/metadata-configuration'; import { WizardStep } from '../../../wizard/model'; import * as utils from '../../domain/utility/configuration'; import { getSplitSchema } from '../../../wizard/reducer'; +import { SectionProperty } from '../model/section'; export const getConfigurationSectionsFn = (models, definition, schema): MetadataConfiguration => { return !definition || !schema || !models ? null : @@ -82,3 +83,23 @@ export const assignValueToProperties = (models, properties, definition: any): an } }); }; + +export const getLimitedPropertiesFn = (properties: SectionProperty[]) => { + return ([ + ...properties + .filter(p => p.differences) + .map(p => { + const parsed = { ...p }; + if (p.widget && p.widget.data) { + parsed.widget = { + ...p.widget, + data: p.widget.data.filter(item => item.differences) + }; + } + if (p.properties) { + parsed.properties = getLimitedPropertiesFn(p.properties); + } + return parsed; + }) + ]); +}; \ No newline at end of file diff --git a/ui/src/app/metadata/configuration/service/configuration.service.spec.ts b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts index 43fd99c05..e191471a0 100644 --- a/ui/src/app/metadata/configuration/service/configuration.service.spec.ts +++ b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts @@ -9,7 +9,7 @@ import { of } from 'rxjs'; import { MetadataProviderService } from '../../domain/service/provider.service'; import { Metadata } from '../../domain/domain.type'; import { SCHEMA } from '../../../../testing/form-schema.stub'; -import { getConfigurationSectionsFn } from '../reducer'; +import { getConfigurationSectionsFn } from '../reducer/utilities'; describe(`Configuration Service`, () => { diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.html b/ui/src/app/metadata/domain/component/wizard-summary.component.html new file mode 100644 index 000000000..349f7cb74 --- /dev/null +++ b/ui/src/app/metadata/domain/component/wizard-summary.component.html @@ -0,0 +1,14 @@ +
+
+
+ + + + +
+
+
\ No newline at end of file diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts b/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts new file mode 100644 index 000000000..43712bd48 --- /dev/null +++ b/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts @@ -0,0 +1,103 @@ +import { Component, ViewChild } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { NgbDropdownModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; + +import { WizardSummaryComponent } from './wizard-summary.component'; +import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-schema-form'; +import { Wizard } from '../../../wizard/model'; +import { MetadataProvider } from '../../domain/model'; +import { SummaryPropertyComponent } from './summary-property.component'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { MetadataProviderWizard } from '../../provider/model'; +import { AttributesService } from '../service/attributes.service'; +import { MockAttributeService } from '../../../../testing/attributes.stub'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(WizardSummaryComponent) + public componentUnderTest: WizardSummaryComponent; + + private _summary; + + get summary(): { definition: Wizard, schema: { [id: string]: any }, model: any } { + return this._summary; + } + + set summary(summary: { definition: Wizard, schema: { [id: string]: any }, model: any }) { + this._summary = summary; + } +} + +describe('Wizard Summary Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: WizardSummaryComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule, + NgbPopoverModule, + RouterTestingModule, + SchemaFormModule.forRoot(), + MockI18nModule + ], + declarations: [ + WizardSummaryComponent, + SummaryPropertyComponent, + TestHostComponent + ], + providers: [ + { provide: WidgetRegistry, useClass: DefaultWidgetRegistry }, + { provide: AttributesService, useClass: MockAttributeService } + ] + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should instantiate the component', async(() => { + expect(app).toBeTruthy(); + })); + + describe('gotoPage function', () => { + it('should emit an empty string if page is null', () => { + spyOn(app.onPageSelect, 'emit'); + app.gotoPage(); + expect(app.onPageSelect.emit).toHaveBeenCalledWith(''); + }); + + it('should emit the provided page', () => { + spyOn(app.onPageSelect, 'emit'); + app.gotoPage('foo'); + expect(app.onPageSelect.emit).toHaveBeenCalledWith('foo'); + }); + }); + + describe('ngOnChanges', () => { + it('should set columns and sections if summary is provided', () => { + instance.summary = { + model: { + name: 'foo', + '@type': 'MetadataProvider' + }, + schema: SCHEMA, + definition: MetadataProviderWizard + }; + fixture.detectChanges(); + expect(app.sections).toBeDefined(); + expect(app.columns).toBeDefined(); + }); + }); +}); diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.ts b/ui/src/app/metadata/domain/component/wizard-summary.component.ts new file mode 100644 index 000000000..73a701491 --- /dev/null +++ b/ui/src/app/metadata/domain/component/wizard-summary.component.ts @@ -0,0 +1,80 @@ +import { Component, Input, SimpleChanges, OnChanges, Output, EventEmitter } from '@angular/core'; + +import { Wizard, WizardStep } from '../../../wizard/model'; +import { MetadataProvider, MetadataResolver } from '../../domain/model'; +import { Property } from '../model/property'; +import { getSplitSchema } from '../../../wizard/reducer'; +import { getStepProperties } from '../utility/configuration'; + +interface Section { + id: string; + index: number; + label: string; + pageNumber: number; + properties: Property[]; +} + +@Component({ + selector: 'wizard-summary', + templateUrl: './wizard-summary.component.html', + styleUrls: [] +}) + +export class WizardSummaryComponent implements OnChanges { + @Input() summary: { definition: Wizard, schema: { [id: string]: any }, model: any }; + + @Output() onPageSelect: EventEmitter = new EventEmitter(); + + sections: Section[]; + columns: Array
[]; + steps: WizardStep[]; + + constructor() { } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.summary && this.summary) { + const schema = this.summary.schema; + const model = this.summary.model; + const def = this.summary.definition; + const steps = def.steps; + + this.sections = steps + .filter(step => step.id !== 'summary') + .map( + (step: WizardStep, num: number) => { + const { id, index, label } = step; + const split = getSplitSchema(schema, step); + const properties = getStepProperties( + split, + def.formatter(model), + schema.definitions || {} + ); + return ({ + id, + pageNumber: num + 1, + index, + label, + properties + }); + } + ); + + this.columns = this.sections.reduce((resultArray, item, index) => { + const chunkIndex = Math.floor(index / Math.round(this.sections.length / 2)); + + if (!resultArray[chunkIndex]) { + resultArray[chunkIndex] = []; + } + + resultArray[chunkIndex].push(item); + + return resultArray; + }, []); + } + } + + gotoPage(page: string = ''): void { + this.onPageSelect.emit(page); + } +} + diff --git a/ui/src/app/metadata/domain/domain.module.ts b/ui/src/app/metadata/domain/domain.module.ts index b109734a6..49e1fe685 100644 --- a/ui/src/app/metadata/domain/domain.module.ts +++ b/ui/src/app/metadata/domain/domain.module.ts @@ -22,8 +22,10 @@ import { UnsavedEntityComponent } from './component/unsaved-entity.dialog'; import { EditorNavComponent } from './component/editor-nav.component'; import { RouterModule } from '@angular/router'; import { SharedModule } from '../../shared/shared.module'; +import { WizardSummaryComponent } from './component/wizard-summary.component'; export const COMPONENTS = [ + WizardSummaryComponent, PreviewDialogComponent, UnsavedEntityComponent, SummaryPropertyComponent, diff --git a/ui/src/app/metadata/provider/model/dynamic-http.provider.form.spec.ts b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.spec.ts index bafe25f4b..f561b4e99 100644 --- a/ui/src/app/metadata/provider/model/dynamic-http.provider.form.spec.ts +++ b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.spec.ts @@ -1,5 +1,51 @@ import { DynamicHttpMetadataProviderWizard } from './dynamic-http.provider.form'; +const schema = { + 'type': 'object', + 'required': [ + '@type', + 'content' + ], + 'properties': { + '@type': { + 'title': 'label.md-request-type', + 'description': 'tooltip.md-request-type', + 'type': 'string', + 'widget': { + 'id': 'select' + }, + 'oneOf': [ + { + 'enum': [ + 'MetadataQueryProtocol' + ], + 'description': 'value.md-query-protocol' + }, + { + 'enum': [ + 'Regex' + ], + 'description': 'value.regex' + } + ] + }, + 'content': { + 'title': 'label.md-request-value', + 'description': 'tooltip.md-request-value', + 'type': 'string' + }, + 'match': { + 'title': 'label.match', + 'description': 'tooltip.match', + 'type': 'string', + 'visibleIf': { + '@type': [ + 'Regex' + ] + } + } + } +}; describe('DynamicHttpMetadataProviderWizard', () => { @@ -122,4 +168,43 @@ describe('DynamicHttpMetadataProviderWizard', () => { ]); }); }); + + describe('validators', () => { + let validators, + metadataRequestURLConstructionScheme, + metadataRequestURLConstructionSchemeContent, + metadataRequestURLConstructionSchemeType, + metadataRequestURLConstructionSchemeMatch; + + beforeEach(() => { + validators = getValidators([], []); + }); + + describe('metadataRequestURLConstructionScheme', () => { + it('should check other validators and propagate those errors up', () => { + const value = { + content: null, + '@type': null, + match: 'foo' + }; + const property = { value, schema, properties: null }; + property.properties = { + content: { + path: 'content', + parent: property + }, + '@type': { + path: '@type', + parent: property + }, + match: { + path: 'match', + parent: property + } + }; + const validator = validators['/metadataRequestURLConstructionScheme']; + expect(validator(value, property, null).length).toBe(2); + }); + }); + }); }); diff --git a/ui/src/app/metadata/provider/model/utilities.spec.ts b/ui/src/app/metadata/provider/model/utilities.spec.ts new file mode 100644 index 000000000..5affea1ae --- /dev/null +++ b/ui/src/app/metadata/provider/model/utilities.spec.ts @@ -0,0 +1,33 @@ +import { metadataFilterProcessor } from './utilities'; + +describe('provider model utilities', () => { + describe('metadata filter processor function', () => { + it('should return null if no schema provided', () => { + expect(metadataFilterProcessor(null)).toBe(null); + }); + + it('should return the schema if no properties are detected', () => { + const schema = {}; + expect(metadataFilterProcessor(schema)).toBe(schema); + }); + + it('should return the schema if no metadataFilters property exists in the schema', () => { + const schema = { properties: { foo: 'bar' } }; + expect(metadataFilterProcessor(schema)).toBe(schema); + }); + + it('should turn the filters into an object if provided ', () => { + const schema = { properties: { metadataFilters: { + type: 'array', + items: [ + { + $id: 'foo', + type: 'string' + } + ] + } } }; + const processed = metadataFilterProcessor(schema); + expect(processed.properties.metadataFilters.properties.foo.type).toBe('string'); + }); + }); +}); diff --git a/ui/src/app/metadata/provider/model/utilities.ts b/ui/src/app/metadata/provider/model/utilities.ts index aea90bc21..dc415bae1 100644 --- a/ui/src/app/metadata/provider/model/utilities.ts +++ b/ui/src/app/metadata/provider/model/utilities.ts @@ -1,5 +1,4 @@ export const metadataFilterProcessor = (schema) => { - console.log(schema); if (!schema) { return null; } @@ -20,6 +19,5 @@ export const metadataFilterProcessor = (schema) => { } } }); - console.log(processed); return processed; }; diff --git a/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts b/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts index 3cec9bf25..58191efeb 100644 --- a/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts +++ b/ui/src/app/metadata/resolver/container/resolver-wizard.component.spec.ts @@ -2,7 +2,7 @@ import { Component, ViewChild } from '@angular/core'; import { TestBed, async, ComponentFixture } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { StoreModule, Store, combineReducers } from '@ngrx/store'; -import { RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'; +import { RouterStateSnapshot } from '@angular/router'; import { NgbDropdownModule, NgbPopoverModule, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { of } from 'rxjs'; @@ -18,6 +18,7 @@ import { MockWizardModule } from '../../../../testing/wizard.stub'; import { NgbModalStub } from '../../../../testing/modal.stub'; import { MetadataResolver } from '../../domain/model'; import { DifferentialService } from '../../../core/service/differential.service'; +import { MetadataConfigurationComponentStub } from '../../../../testing/metadata-configuration.stub'; @Component({ template: ` @@ -85,7 +86,8 @@ describe('Resolver Wizard Component', () => { ], declarations: [ ResolverWizardComponent, - TestHostComponent + TestHostComponent, + MetadataConfigurationComponentStub ], providers: [ DifferentialService, diff --git a/ui/src/app/wizard/reducer/index.ts b/ui/src/app/wizard/reducer/index.ts index 027ac1673..255710be0 100644 --- a/ui/src/app/wizard/reducer/index.ts +++ b/ui/src/app/wizard/reducer/index.ts @@ -129,7 +129,7 @@ export const getSchemaLockedFn = (step, locked) => step ? step.locked ? locked : export const getLocked = createSelector(getCurrent, getLockedStatus, getSchemaLockedFn); export const getSchemaProcessedFn = (schema, definition) => - definition.schemaPreprocessor ? definition.schemaPreprocessor(schema) : schema; + definition && definition.schemaPreprocessor ? definition.schemaPreprocessor(schema) : schema; export const getSchemaObject = createSelector(getState, fromWizard.getSchema); export const getProcessedSchema = createSelector(getSchemaObject, getWizardDefinition, getSchemaProcessedFn); From 22cb57c17c7b6d8ce6d69110dfb2aa268a1a9c26 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 30 Sep 2019 10:16:59 -0400 Subject: [PATCH 05/12] SHIBUI-1501 --- ...lerVersionEndpointsIntegrationTests.groovy | 67 +++++++++++++++++++ ...MetadataFilterEnversVersioningTests.groovy | 43 ++++++++++++ .../MetadataResolversController.java | 1 + .../ui/domain/resolvers/MetadataResolver.java | 11 +++ .../EnversMetadataResolverVersionService.java | 13 +++- .../util/AttributeReleaseAndOverridesX.groovy | 13 ++++ 6 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy index 8e23362e7..0e10dbac9 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy @@ -1,5 +1,9 @@ package edu.internet2.tier.shibboleth.admin.ui.controller +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilterTarget import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter @@ -11,10 +15,15 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadat import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository +import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService +import edu.internet2.tier.shibboleth.admin.ui.util.AttributeReleaseAndOverridesX +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.web.client.TestRestTemplate import org.springframework.test.context.ActiveProfiles +import org.springframework.transaction.PlatformTransactionManager import spock.lang.Specification import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.ENTITY @@ -32,12 +41,34 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi @Autowired MetadataResolverRepository repository + @Autowired + AttributeUtility attributeUtility + + @Autowired + CustomPropertiesConfiguration customPropertiesConfiguration + + ObjectMapper mapper + TestObjectGenerator generator + + @Autowired + PlatformTransactionManager txMgr + + @Autowired + MetadataResolverVersionService metadataResolverVersionService + static BASE_URI = '/api/MetadataResolvers' static ALL_VERSIONS_URI = "$BASE_URI/%s/Versions" static SPECIFIC_VERSION_URI = "$BASE_URI/%s/Versions/%s" + def setup() { + generator = new TestObjectGenerator(attributeUtility, customPropertiesConfiguration) + mapper = new ObjectMapper() + mapper.enable(SerializationFeature.INDENT_OUTPUT) + mapper.registerModule(new JavaTimeModule()) + } + def "GET /api/MetadataResolvers/{resourceId}/Versions with non-existent resolver"() { when: def result = getAllMetadataResolverVersions('non-existent-resolver-id', String) @@ -111,6 +142,7 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi } def "SHIBUI-1386"() { + given: MetadataResolver mr = new FileBackedHttpMetadataResolver(name: 'testme') mr = repository.save(mr) @@ -142,6 +174,7 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi } def "SHIBUI-1500"() { + given: MetadataResolver mr = new FileBackedHttpMetadataResolver(name: 'shibui-1500') mr = repository.save(mr) @@ -159,6 +192,7 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi } def "SHIBUI-1499"() { + given: MetadataResolver mr = new FileBackedHttpMetadataResolver(name: 'shibui-1499') mr = repository.save(mr) @@ -182,6 +216,29 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi noExceptionThrown() } + def "SHIBUI-1501"() { + given: + def mr = new FileBackedHttpMetadataResolver(name: 'shibui-1501') + mr = repository.save(mr) + + when: 'add a filter' + EntityAttributesFilter filter = this.generator.entityAttributesFilter() + mr.addFilter(filter) + def resolver = (repository.save(mr) as MetadataResolver).withTraits AttributeReleaseAndOverrides + resolver.entityAttributesFilterIntoTransientRepresentation() + + def allVersions = getAllMetadataResolverVersions(mr.resourceId, List) + def mrv2 = getMetadataResolverForVersion(mr.resourceId, allVersions.body[1].id, MetadataResolver) + .body.withTraits AttributeReleaseAndOverrides + + then: + mrv2.metadataFilters.size() == 1 + mrv2.attributesRelease(0).size() == resolver.attributesRelease(0).size() + mrv2.overrides(0).size() == resolver.overrides(0).size() + mrv2.attributesRelease(0) == resolver.attributesRelease(0) + mrv2.overrides(0) == resolver.overrides(0) + } + private getAllMetadataResolverVersions(String resourceId, responseType) { this.restTemplate.getForEntity(resourceUriFor(ALL_VERSIONS_URI, resourceId), responseType) } @@ -198,3 +255,13 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi String.format(uriTemplate, resourceId) } } + +trait AttributeReleaseAndOverrides { + List attributesRelease(int filterIndex) { + (this.metadataFilters[filterIndex] as EntityAttributesFilter).attributeRelease + } + + Map overrides(int filterIndex) { + (this.metadataFilters[filterIndex] as EntityAttributesFilter).relyingPartyOverrides + } +} diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy index fe1e5eee4..366dc9c0d 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy @@ -1,6 +1,10 @@ package edu.internet2.tier.shibboleth.admin.ui.repository.envers +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import edu.internet2.tier.shibboleth.admin.ui.configuration.CoreShibUiConfiguration +import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.EntitiesVersioningConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.InternationalizationConfiguration import edu.internet2.tier.shibboleth.admin.ui.configuration.SearchConfiguration @@ -19,6 +23,8 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.ResourceBackedMet import edu.internet2.tier.shibboleth.admin.ui.repository.FilterRepository import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService +import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator +import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.domain.EntityScan import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest @@ -49,6 +55,22 @@ class MetadataFilterEnversVersioningTests extends Specification { @Autowired PlatformTransactionManager txMgr + @Autowired + AttributeUtility attributeUtility + + @Autowired + CustomPropertiesConfiguration customPropertiesConfiguration + + ObjectMapper mapper + TestObjectGenerator generator + + def setup() { + generator = new TestObjectGenerator(attributeUtility, customPropertiesConfiguration) + mapper = new ObjectMapper() + mapper.enable(SerializationFeature.INDENT_OUTPUT) + mapper.registerModule(new JavaTimeModule()) + } + def "test versioning of MetadataResolver with EntityRoleWhiteListFilter"() { when: 'Add initial filter' @@ -312,4 +334,25 @@ class MetadataFilterEnversVersioningTests extends Specification { mrv1.metadataFilters.size() == 1 mrv2.metadataFilters.size() == 0 } + + def "SHIBUI-1501"() { + when: 'Add initial filter' + def mr = new FileBackedHttpMetadataResolver(name: 'resolver') + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + EntityAttributesFilter filter = this.generator.entityAttributesFilter() + mr.metadataFilters.add(filter) + mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { + metadataResolverRepository.save(mr) + } + def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) + def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) + def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) + + then: + versions.size() == 2 + mrv1.metadataFilters.size() == 0 + mrv2.metadataFilters.size() == 1 + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java index dbdc0ddd1..e07272629 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolversController.java @@ -170,6 +170,7 @@ public ResponseEntity getAllVersions(@PathVariable String resourceId) { } @GetMapping("/MetadataResolvers/{resourceId}/Versions/{versionId}") + @Transactional(readOnly = true) public ResponseEntity getSpecificVersion(@PathVariable String resourceId, @PathVariable String versionId) { MetadataResolver resolver = versionService.findSpecificVersionOfMetadataResolver(resourceId, versionId); if (resolver == null) { diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java index 9e67a5549..d24cd3638 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataResolver.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import edu.internet2.tier.shibboleth.admin.ui.domain.AbstractAuditable; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.filters.MetadataFilter; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -100,4 +101,14 @@ public void addFilter(MetadataFilter metadataFilter) { public void markAsModified() { this.versionModifiedTimestamp = System.currentTimeMillis(); } + + public void entityAttributesFilterIntoTransientRepresentation() { + //expose explicit API to call to convert into transient representation + //used in unit/integration tests where JPA's @PostLoad callback execution engine is not available + this.metadataFilters + .stream() + .filter(EntityAttributesFilter.class::isInstance) + .map(EntityAttributesFilter.class::cast) + .forEach(EntityAttributesFilter::intoTransientRepresentation); + } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java index c98203eca..b0d90195f 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/service/EnversMetadataResolverVersionService.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.service; +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter; import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver; import edu.internet2.tier.shibboleth.admin.ui.domain.versioning.Version; import edu.internet2.tier.shibboleth.admin.ui.envers.EnversVersionServiceSupport; @@ -27,6 +28,16 @@ public List findVersionsForMetadataResolver(String resourceId) { @Override public MetadataResolver findSpecificVersionOfMetadataResolver(String resourceId, String versionId) { Object mrObject = enversVersionServiceSupport.findSpecificVersionOfPersistentEntity(resourceId, versionId, MetadataResolver.class); - return mrObject == null ? null : (MetadataResolver) mrObject; + if(mrObject == null) { + return null; + } + MetadataResolver resolver = (MetadataResolver) mrObject; + + //The @PostLoad is not honored by Envers. So need to do this manually for EntityAttributesFilters + //So the correct representation is built and returned to upstream clients expecting JSON + resolver.entityAttributesFilterIntoTransientRepresentation(); + + return resolver; + } } diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy new file mode 100644 index 000000000..d3198d225 --- /dev/null +++ b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy @@ -0,0 +1,13 @@ +package edu.internet2.tier.shibboleth.admin.ui.util + +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter + +trait AttributeReleaseAndOverridesX { + List attributesRelease(int filterIndex) { + (this.metadataFilters[filterIndex] as EntityAttributesFilter).attributeRelease + } + + Map overrides() { + (this.metadataFilters[filterIndex] as EntityAttributesFilter).relyingPartyOverrides + } +} From f4ce06f2fb3dad4a15101bf626df7a6d13a80eba Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 30 Sep 2019 10:18:04 -0400 Subject: [PATCH 06/12] SHIBUI-1501: cleanup --- .../ui/util/AttributeReleaseAndOverridesX.groovy | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy diff --git a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy b/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy deleted file mode 100644 index d3198d225..000000000 --- a/backend/src/test/groovy/edu/internet2/tier/shibboleth/admin/ui/util/AttributeReleaseAndOverridesX.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package edu.internet2.tier.shibboleth.admin.ui.util - -import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFilter - -trait AttributeReleaseAndOverridesX { - List attributesRelease(int filterIndex) { - (this.metadataFilters[filterIndex] as EntityAttributesFilter).attributeRelease - } - - Map overrides() { - (this.metadataFilters[filterIndex] as EntityAttributesFilter).relyingPartyOverrides - } -} From 21ef140bf25b4654308a579d158f7bb77e14b2c7 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 30 Sep 2019 10:24:52 -0400 Subject: [PATCH 07/12] SHIBUI-1501: cleanup --- ...dataResolverControllerVersionEndpointsIntegrationTests.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy index 0e10dbac9..d1bf151d3 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy @@ -16,7 +16,6 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetad import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService -import edu.internet2.tier.shibboleth.admin.ui.util.AttributeReleaseAndOverridesX import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility import org.springframework.beans.factory.annotation.Autowired From 25bc0af217bbb8a2cc4d8b855f054d2c7f17a896 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Mon, 30 Sep 2019 11:21:33 -0400 Subject: [PATCH 08/12] Fix tests --- ...MetadataFilterEnversVersioningTests.groovy | 21 ------------------- .../filters/EntityAttributesFilter.java | 3 +++ 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy index 366dc9c0d..6df7da461 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/repository/envers/MetadataFilterEnversVersioningTests.groovy @@ -334,25 +334,4 @@ class MetadataFilterEnversVersioningTests extends Specification { mrv1.metadataFilters.size() == 1 mrv2.metadataFilters.size() == 0 } - - def "SHIBUI-1501"() { - when: 'Add initial filter' - def mr = new FileBackedHttpMetadataResolver(name: 'resolver') - mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { - metadataResolverRepository.save(mr) - } - EntityAttributesFilter filter = this.generator.entityAttributesFilter() - mr.metadataFilters.add(filter) - mr = EnversTestsSupport.doInExplicitTransaction(txMgr) { - metadataResolverRepository.save(mr) - } - def versions = metadataResolverVersionService.findVersionsForMetadataResolver(mr.resourceId) - def mrv1 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[0].id) - def mrv2 = metadataResolverVersionService.findSpecificVersionOfMetadataResolver(mr.resourceId, versions[1].id) - - then: - versions.size() == 2 - mrv1.metadataFilters.size() == 0 - mrv2.metadataFilters.size() == 1 - } } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java index 5d6d3132a..a3e0a6a93 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/filters/EntityAttributesFilter.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromAttributeReleaseList; import static edu.internet2.tier.shibboleth.admin.util.ModelRepresentationConversions.getAttributeListFromRelyingPartyOverridesRepresentation; @@ -69,6 +70,8 @@ private void rebuildAttributes() { @PostLoad public void intoTransientRepresentation() { + //For some update operations, list of attributes could contain null values. Filter them out + this.attributes.removeIf(Objects::isNull); this.attributeRelease = getAttributeReleaseListFromAttributeList(this.attributes); this.relyingPartyOverrides = getRelyingPartyOverridesRepresentationFromAttributeList(this.attributes); } From d1833a995bef48f4aac994e130ba5d84729955ea Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 1 Oct 2019 12:40:05 -0700 Subject: [PATCH 09/12] SHIBUI-1511 Fixed issue with filters showing for providers --- .../metadata-comparison.component.html | 43 ++++++++----------- .../metadata-comparison.component.ts | 11 ++--- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html index effbefb0a..2c6415052 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html @@ -25,32 +25,27 @@

- -
-

- Metadata Filter -

-
- - - - - -
- -
- + +
+

+ Metadata Filter +

+ + + + + +
+ +
+ +
+
-
diff --git a/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts b/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts index e8a905b0d..8fb4a817b 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts @@ -20,14 +20,15 @@ export class MetadataComparisonComponent implements OnDestroy { limiter: BehaviorSubject = new BehaviorSubject(false); - versions$: Observable; - numVersions$: Observable; - type$: Observable; + versions$: Observable = this.store.select(fromReducer.getLimitedComparisonConfigurations); + numVersions$: Observable = this.store.select(getComparisonConfigurationCount); + type$: Observable = this.store.select(fromReducer.getConfigurationModelType); loading$: Observable = this.store.select(fromReducer.getComparisonLoading); limited$: Observable = this.store.select(fromReducer.getViewChangedOnly); sub: Subscription; filters$: Observable = this.store.select(fromReducer.getComparisonFilterConfiguration); filterCompare$: Observable = this.store.select(fromReducer.getLimitedFilterComparisonConfiguration); + isProvider$: Observable = this.type$.pipe(map(t => t !== 'resolver')); constructor( private store: Store, @@ -39,10 +40,6 @@ export class MetadataComparisonComponent implements OnDestroy { map(versions => new CompareVersionRequest(versions)) ).subscribe(this.store); - this.versions$ = this.store.select(fromReducer.getLimitedComparisonConfigurations); - this.numVersions$ = this.store.select(getComparisonConfigurationCount); - this.type$ = this.store.select(fromReducer.getConfigurationModelType); - this.sub = this.limiter.pipe( withLatestFrom(this.limited$), map(([compare, limit]) => new ViewChanged(!limit)) From d64ed696ded5138afaf44aae3767eab078e9ca92 Mon Sep 17 00:00:00 2001 From: Dmitriy Kopylenko Date: Tue, 1 Oct 2019 16:28:39 -0400 Subject: [PATCH 10/12] SHIBUI-1509 --- ...lerVersionEndpointsIntegrationTests.groovy | 29 +++++++++++++++++++ .../MetadataRequestURLConstructionScheme.java | 2 ++ 2 files changed, 31 insertions(+) diff --git a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy index d1bf151d3..8cb3df104 100644 --- a/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy +++ b/backend/src/enversTest/groovy/edu/internet2/tier/shibboleth/admin/ui/controller/MetadataResolverControllerVersionEndpointsIntegrationTests.groovy @@ -9,23 +9,33 @@ import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityAttributesFil import edu.internet2.tier.shibboleth.admin.ui.domain.filters.EntityRoleWhiteListFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilter import edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget +import edu.internet2.tier.shibboleth.admin.ui.domain.filters.SignatureValidationFilter import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.DynamicHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FileBackedHttpMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.FilesystemMetadataResolver import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.LocalDynamicMetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataQueryProtocolScheme import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.MetadataResolver +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.RegexScheme +import edu.internet2.tier.shibboleth.admin.ui.domain.resolvers.TemplateScheme import edu.internet2.tier.shibboleth.admin.ui.repository.MetadataResolverRepository + import edu.internet2.tier.shibboleth.admin.ui.service.MetadataResolverVersionService import edu.internet2.tier.shibboleth.admin.ui.util.TestObjectGenerator import edu.internet2.tier.shibboleth.admin.util.AttributeUtility + +import org.apache.commons.lang3.RandomStringUtils + import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.web.client.TestRestTemplate import org.springframework.test.context.ActiveProfiles import org.springframework.transaction.PlatformTransactionManager import spock.lang.Specification +import spock.lang.Unroll import static edu.internet2.tier.shibboleth.admin.ui.domain.filters.NameIdFormatFilterTarget.NameIdFormatFilterTargetType.ENTITY +import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic /** * @author Dmitriy Kopylenko @@ -238,6 +248,25 @@ class MetadataResolverControllerVersionEndpointsIntegrationTests extends Specifi mrv2.overrides(0) == resolver.overrides(0) } + @Unroll + def "SHIBUI-1509 with #urlConstructionScheme"() { + MetadataResolver mr = new DynamicHttpMetadataResolver(name: randomAlphabetic(8)).with { + it.metadataRequestURLConstructionScheme = urlConstructionScheme + it + } + mr = repository.save(mr) + + when: + def allVersions = getAllMetadataResolverVersions(mr.resourceId, List) + def mrv1 = getMetadataResolverForVersion(mr.resourceId, allVersions.body[0].id, MetadataResolver) + + then: + noExceptionThrown() + + where: + urlConstructionScheme << [new RegexScheme(match: ".*"), new MetadataQueryProtocolScheme(), new TemplateScheme()] + } + private getAllMetadataResolverVersions(String resourceId, responseType) { this.restTemplate.getForEntity(resourceUriFor(ALL_VERSIONS_URI, resourceId), responseType) } diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java index af3b0eab5..a1223840e 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/domain/resolvers/MetadataRequestURLConstructionScheme.java @@ -1,5 +1,6 @@ package edu.internet2.tier.shibboleth.admin.ui.domain.resolvers; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -28,6 +29,7 @@ @JsonSubTypes.Type(value=RegexScheme.class, name="Regex")}) @Audited @AuditOverride(forClass = AbstractAuditable.class) +@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"}) public abstract class MetadataRequestURLConstructionScheme extends AbstractAuditable { public enum SchemeType { METADATA_QUERY_PROTOCOL("MetadataQueryProtocol"), From c316358f1cb5e42a8f70fa4473bf1a92620fbe49 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 1 Oct 2019 13:46:13 -0700 Subject: [PATCH 11/12] SHIBUI-1511 --- .../configuration/container/metadata-comparison.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html index a12fb59c5..01617fc64 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html @@ -53,7 +53,7 @@


- +
-
+
+

No Filters

+

No filters have been added to this Metadata Provider

+
+
-
-

No Filters

-

No filters have been added to this Metadata Provider

-
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html index 6214c37d2..df06cc729 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html @@ -37,9 +37,4 @@

- - - \ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html index 01617fc64..a3505ae44 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html @@ -39,28 +39,13 @@


- +
- - - - - -
- -
- -
-
diff --git a/ui/src/app/metadata/configuration/reducer/utilities.ts b/ui/src/app/metadata/configuration/reducer/utilities.ts index f2481e99c..dfcad151e 100644 --- a/ui/src/app/metadata/configuration/reducer/utilities.ts +++ b/ui/src/app/metadata/configuration/reducer/utilities.ts @@ -44,7 +44,15 @@ export const assignValueToProperties = (models, properties, definition: any): an if (!array) { return false; } - return JSON.stringify(model[prop.id]) !== JSON.stringify(array[0][prop.id]); + let prop1 = model[prop.id]; + let prop2 = array[0][prop.id]; + if (prop1 && prop1.modifiedDate) { + let { checkedModifiedDate, checkedProp } = prop1; + let { firstModifiedDate, firstProp } = prop2; + prop1 = checkedProp; + prop2 = firstProp; + } + return JSON.stringify(prop1) !== JSON.stringify(prop2); }); const widget = prop.type === 'array' && prop.widget && prop.widget.data ? ({ diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.html b/ui/src/app/metadata/provider/container/provider-wizard.component.html index 58766e536..366bb70d5 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.html +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.html @@ -19,7 +19,11 @@ - + + 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 f501f7cb8..e812527d5 100644 --- a/ui/src/app/metadata/provider/container/provider-wizard.component.ts +++ b/ui/src/app/metadata/provider/container/provider-wizard.component.ts @@ -63,7 +63,7 @@ export class ProviderWizardComponent implements OnDestroy { this.summary$ = combineLatest( this.store.select(fromWizard.getWizardDefinition), - this.store.select(fromWizard.getSchemaObject), + this.store.select(fromWizard.getParsedSchema), this.store.select(fromProvider.getEntityChanges) ).pipe( map(([ definition, schema, model ]) => ({ definition, schema, model }))