From 7527e8ebbcd17fa9e16c3d93f85c792895f38da3 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Wed, 29 May 2019 13:10:56 -0700 Subject: [PATCH 01/10] initial commit for SHIBUI-1267 --- .../action/configuration.action.ts | 61 +++++++++++++++ .../metadata-configuration.component.html | 10 +++ .../metadata-configuration.component.spec.ts | 0 .../metadata-configuration.component.ts | 16 ++++ .../configuration/configuration.module.ts | 45 +++++++++++ .../configuration/configuration.routing.ts | 9 +++ .../container/configuration.component.html | 17 +++++ .../container/configuration.component.spec.ts | 0 .../container/configuration.component.ts | 44 +++++++++++ .../effect/configuration.effect.ts | 48 ++++++++++++ .../model/metadata-configuration.ts | 5 ++ .../metadata/configuration/model/schema.ts | 3 + .../metadata/configuration/model/section.ts | 11 +++ .../reducer/configuration.reducer.ts | 51 +++++++++++++ .../metadata/configuration/reducer/index.ts | 75 +++++++++++++++++++ .../service/configuration.service.ts | 35 +++++++++ .../metadata/configuration/service/utility.ts | 40 ++++++++++ .../model/wizards/metadata-source-editor.ts | 2 +- .../dashboard-resolvers-list.component.html | 2 +- .../dashboard-resolvers-list.component.ts | 4 +- ui/src/app/metadata/metadata.module.ts | 3 +- ui/src/app/metadata/metadata.routing.ts | 4 +- 22 files changed, 479 insertions(+), 6 deletions(-) create mode 100644 ui/src/app/metadata/configuration/action/configuration.action.ts create mode 100644 ui/src/app/metadata/configuration/component/metadata-configuration.component.html create mode 100644 ui/src/app/metadata/configuration/component/metadata-configuration.component.spec.ts create mode 100644 ui/src/app/metadata/configuration/component/metadata-configuration.component.ts create mode 100644 ui/src/app/metadata/configuration/configuration.module.ts create mode 100644 ui/src/app/metadata/configuration/configuration.routing.ts create mode 100644 ui/src/app/metadata/configuration/container/configuration.component.html create mode 100644 ui/src/app/metadata/configuration/container/configuration.component.spec.ts create mode 100644 ui/src/app/metadata/configuration/container/configuration.component.ts create mode 100644 ui/src/app/metadata/configuration/effect/configuration.effect.ts create mode 100644 ui/src/app/metadata/configuration/model/metadata-configuration.ts create mode 100644 ui/src/app/metadata/configuration/model/schema.ts create mode 100644 ui/src/app/metadata/configuration/model/section.ts create mode 100644 ui/src/app/metadata/configuration/reducer/configuration.reducer.ts create mode 100644 ui/src/app/metadata/configuration/reducer/index.ts create mode 100644 ui/src/app/metadata/configuration/service/configuration.service.ts create mode 100644 ui/src/app/metadata/configuration/service/utility.ts diff --git a/ui/src/app/metadata/configuration/action/configuration.action.ts b/ui/src/app/metadata/configuration/action/configuration.action.ts new file mode 100644 index 000000000..42d1dddaa --- /dev/null +++ b/ui/src/app/metadata/configuration/action/configuration.action.ts @@ -0,0 +1,61 @@ +import { Action } from '@ngrx/store'; +import { Metadata } from '../../domain/domain.type'; +import { Schema } from '../model/schema'; +import { Wizard } from '../../../wizard/model'; + +export enum ConfigurationActionTypes { + LOAD_METADATA_REQUEST = '[Metadata Configuration] Load Metadata Request', + LOAD_METADATA_SUCCESS = '[Metadata Configuration] Load Metadata Success', + LOAD_METADATA_ERROR = '[Metadata Configuration] Load Metadata Error', + + SET_METADATA = '[Metadata Configuration] Set Metadata Model', + SET_DEFINITION = '[Metadata Configuration] Set Metadata Definition', + SET_SCHEMA = '[Metadata Configuration] Set Metadata Schema', + CLEAR = '[Metadata Configuration] Clear' +} + +export class LoadMetadataRequest implements Action { + readonly type = ConfigurationActionTypes.LOAD_METADATA_REQUEST; + + constructor(public payload: { id: string, type: string }) { } +} + +export class LoadMetadataSuccess implements Action { + readonly type = ConfigurationActionTypes.LOAD_METADATA_SUCCESS; + + constructor(public payload: Metadata) { } +} + +export class LoadMetadataError implements Action { + readonly type = ConfigurationActionTypes.LOAD_METADATA_ERROR; + + constructor(public payload: any) { } +} + +export class SetMetadata implements Action { + readonly type = ConfigurationActionTypes.SET_METADATA; + + constructor(public payload: Metadata) { } +} + +export class SetDefinition implements Action { + readonly type = ConfigurationActionTypes.SET_DEFINITION; + + constructor(public payload: Wizard) { } +} + +export class SetSchema implements Action { + readonly type = ConfigurationActionTypes.SET_SCHEMA; + + constructor(public payload: Schema) { } +} + +export class ClearConfiguration implements Action { + readonly type = ConfigurationActionTypes.CLEAR; +} + +export type ConfigurationActionsUnion = + | SetMetadata + | SetDefinition + | SetSchema + | ClearConfiguration; diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html new file mode 100644 index 000000000..f2034d06b --- /dev/null +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html @@ -0,0 +1,10 @@ +
+
+
+ {{ i + 1 }}: {{ section.label }} + + {{ prop | json }} + +
+
+
\ No newline at end of file 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 new file mode 100644 index 000000000..e69de29bb diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts new file mode 100644 index 000000000..578b7abd3 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts @@ -0,0 +1,16 @@ +import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { WizardStep } from '../../../wizard/model'; +import Section from '../model/section'; +import { MetadataConfiguration } from '../model/metadata-configuration'; + +@Component({ + selector: 'metadata-configuration', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './metadata-configuration.component.html', + styleUrls: [] +}) +export class MetadataConfigurationComponent { + @Input() configuration: MetadataConfiguration; + + constructor() { } +} diff --git a/ui/src/app/metadata/configuration/configuration.module.ts b/ui/src/app/metadata/configuration/configuration.module.ts new file mode 100644 index 000000000..c04ab7add --- /dev/null +++ b/ui/src/app/metadata/configuration/configuration.module.ts @@ -0,0 +1,45 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { StoreModule } from '@ngrx/store'; +import { EffectsModule } from '@ngrx/effects'; + +import { I18nModule } from '../../i18n/i18n.module'; +import { MetadataConfigurationComponent } from './component/metadata-configuration.component'; +import { ConfigurationComponent } from './container/configuration.component'; +import { MetadataConfigurationService } from './service/configuration.service'; +import * as fromConfig from './reducer'; +import { MetadataConfigurationEffects } from './effect/configuration.effect'; + +@NgModule({ + declarations: [ + MetadataConfigurationComponent, + ConfigurationComponent + ], + entryComponents: [], + imports: [ + CommonModule, + I18nModule + ], + exports: [], + providers: [] +}) +export class MetadataConfigurationModule { + static forRoot(): ModuleWithProviders { + return { + ngModule: RootMetadataConfigurationModule, + providers: [ + MetadataConfigurationService + ] + }; + } +} + +@NgModule({ + imports: [ + MetadataConfigurationModule, + StoreModule.forFeature('metadata-configuration', fromConfig.reducers), + EffectsModule.forFeature([MetadataConfigurationEffects]) + ], + providers: [] +}) +export class RootMetadataConfigurationModule { } diff --git a/ui/src/app/metadata/configuration/configuration.routing.ts b/ui/src/app/metadata/configuration/configuration.routing.ts new file mode 100644 index 000000000..c1e6e138d --- /dev/null +++ b/ui/src/app/metadata/configuration/configuration.routing.ts @@ -0,0 +1,9 @@ +import { Routes } from '@angular/router'; +import { ConfigurationComponent } from './container/configuration.component'; + +export const ConfigurationRoutes: Routes = [ + { + path: ':type/:id/configuration', + component: ConfigurationComponent + } +]; diff --git a/ui/src/app/metadata/configuration/container/configuration.component.html b/ui/src/app/metadata/configuration/container/configuration.component.html new file mode 100644 index 000000000..21e8a3311 --- /dev/null +++ b/ui/src/app/metadata/configuration/container/configuration.component.html @@ -0,0 +1,17 @@ +
+
+
+
+
+ + + Metadata resolver history + +
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts new file mode 100644 index 000000000..e69de29bb diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts new file mode 100644 index 000000000..3c47e0b93 --- /dev/null +++ b/ui/src/app/metadata/configuration/container/configuration.component.ts @@ -0,0 +1,44 @@ +import { Store } from '@ngrx/store'; +import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; + +import * as fromConfiguration from '../reducer'; +import { MetadataConfiguration } from '../model/metadata-configuration'; +import { METADATA_SOURCE_EDITOR } from '../../resolver/wizard-definition'; +import { takeUntil, map } from 'rxjs/operators'; +import { LoadMetadataRequest, ClearConfiguration } from '../action/configuration.action'; + +@Component({ + selector: 'configuration-page', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './configuration.component.html', + styleUrls: [] +}) +export class ConfigurationComponent implements OnDestroy { + private ngUnsubscribe: Subject = new Subject(); + + configuration$: Observable; + + constructor( + private store: Store, + private routerState: ActivatedRoute + ) { + this.configuration$ = this.store.select(fromConfiguration.getConfigurationColumns); + + this.routerState.params.pipe( + takeUntil(this.ngUnsubscribe), + map(params => new LoadMetadataRequest({id: params.id, type: params.type})) + ).subscribe(store); + + this.configuration$.subscribe(c => console.log(c)); + + // this.resolver$ = this.store.select(fromResolvers.getSelectedResolver).pipe(skipWhile(p => !p)); + } + + ngOnDestroy() { + this.ngUnsubscribe.next(); + this.ngUnsubscribe.complete(); + this.store.dispatch(new ClearConfiguration()); + } +} diff --git a/ui/src/app/metadata/configuration/effect/configuration.effect.ts b/ui/src/app/metadata/configuration/effect/configuration.effect.ts new file mode 100644 index 000000000..b3c8f0515 --- /dev/null +++ b/ui/src/app/metadata/configuration/effect/configuration.effect.ts @@ -0,0 +1,48 @@ +import { Injectable } from '@angular/core'; +import { Effect, Actions, ofType } from '@ngrx/effects'; +import { switchMap, catchError, map } from 'rxjs/operators'; +import { of } from 'rxjs'; + +import { MetadataConfigurationService } from '../service/configuration.service'; +import { + LoadMetadataRequest, + LoadMetadataSuccess, + LoadMetadataError, + ConfigurationActionTypes, + SetMetadata, + SetDefinition +} from '../action/configuration.action'; + +@Injectable() +export class MetadataConfigurationEffects { + + @Effect() + loadMetadata$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.LOAD_METADATA_REQUEST), + switchMap(action => + this.configService + .find(action.payload.id, action.payload.type) + .pipe( + map(md => new LoadMetadataSuccess(md)), + catchError(error => of(new LoadMetadataError(error))) + ) + ) + ); + + @Effect() + setMetadataOnLoad$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.LOAD_METADATA_SUCCESS), + map(action => new SetMetadata(action.payload)) + ); + + @Effect() + setDefinitionOnMetadataSet$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.SET_METADATA), + map(action => new SetDefinition(this.configService.getDefinition(action.payload))) + ); + + constructor( + private configService: MetadataConfigurationService, + private actions$: Actions + ) { } +} diff --git a/ui/src/app/metadata/configuration/model/metadata-configuration.ts b/ui/src/app/metadata/configuration/model/metadata-configuration.ts new file mode 100644 index 000000000..10768f14e --- /dev/null +++ b/ui/src/app/metadata/configuration/model/metadata-configuration.ts @@ -0,0 +1,5 @@ +import Section from './section'; + +export interface MetadataConfiguration { + columns: Array
[]; +} diff --git a/ui/src/app/metadata/configuration/model/schema.ts b/ui/src/app/metadata/configuration/model/schema.ts new file mode 100644 index 000000000..469650111 --- /dev/null +++ b/ui/src/app/metadata/configuration/model/schema.ts @@ -0,0 +1,3 @@ +export interface Schema { + [prop: string]: any; +} diff --git a/ui/src/app/metadata/configuration/model/section.ts b/ui/src/app/metadata/configuration/model/section.ts new file mode 100644 index 000000000..089a1953a --- /dev/null +++ b/ui/src/app/metadata/configuration/model/section.ts @@ -0,0 +1,11 @@ +import { Property } from '../../domain/model/property'; + +export interface Section { + id: string; + index: number; + label: string; + pageNumber: number; + properties: Property[]; +} + +export default Section; diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts new file mode 100644 index 000000000..3acc42338 --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts @@ -0,0 +1,51 @@ +import { ConfigurationActionTypes, ConfigurationActionsUnion } from '../action/configuration.action'; +import { Metadata } from '../../domain/domain.type'; +import { Wizard, WizardStep } from '../../../wizard/model'; +import { Schema } from '../model/schema'; + +export interface State { + model: Metadata; + schema: Schema; + definition: Wizard; +} + +export const initialState: State = { + model: null, + schema: null, + definition: null +}; + +export function reducer(state = initialState, action: ConfigurationActionsUnion): State { + switch (action.type) { + case ConfigurationActionTypes.SET_SCHEMA: + return { + ...state, + schema: action.payload + }; + break; + case ConfigurationActionTypes.SET_DEFINITION: + return { + ...state, + definition: action.payload + }; + break; + case ConfigurationActionTypes.SET_METADATA: + return { + ...state, + model: action.payload + }; + break; + case ConfigurationActionTypes.CLEAR: + return { + ...initialState + }; + break; + default: { + return state; + } + } +} + +export const getModel = (state: State) => state.model; +export const getDefinition = (state: State) => state.definition; +export const getSchema = (state: State) => state.schema; diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts new file mode 100644 index 000000000..13b5c627f --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -0,0 +1,75 @@ +import { createSelector, createFeatureSelector } from '@ngrx/store'; +import merge from 'deepmerge'; + +import * as fromRoot from '../../../app.reducer'; +import * as fromConfiguration from './configuration.reducer'; +import { WizardStep } from '../../../wizard/model'; + +import * as utils from '../service/utility'; +import { getSplitSchema } from '../../../wizard/reducer'; + +export interface ConfigurationState { + configuration: fromConfiguration.State; +} + +export const reducers = { + configuration: fromConfiguration.reducer +}; + +export interface State extends fromRoot.State { + 'metadata-configuration': ConfigurationState; +} + +export const getState = createFeatureSelector('metadata-configuration'); + +export const getConfigurationStateFn = (state: ConfigurationState) => state.configuration; + +export const getConfigurationState = createSelector(getState, getConfigurationStateFn); +export const getConfigurationModel = createSelector(getConfigurationState, fromConfiguration.getModel); +export const getConfigurationDefinition = createSelector(getConfigurationState, fromConfiguration.getDefinition); +export const getConfigurationSchema = createSelector(getConfigurationState, fromConfiguration.getSchema); + +export const mergedSchema = createSelector(getConfigurationSchema, schema => !schema ? null : Object.keys(schema).reduce((coll, key) => ({ + ...merge(coll, schema[key]) +}), {} as any)); + +export const getConfigurationSectionsFn = (model, definition, schema) => !definition || !schema ? null : + 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(model), + schema.definitions || {} + ) + }); + } + ); + +export const getConfigurationColumnsFn = sections => !sections ? null : + 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; + }, []); + +export const getConfigurationSections = createSelector( + getConfigurationModel, + getConfigurationDefinition, + mergedSchema, + getConfigurationSectionsFn +); + +export const getConfigurationColumns = createSelector(getConfigurationSections, getConfigurationColumnsFn); diff --git a/ui/src/app/metadata/configuration/service/configuration.service.ts b/ui/src/app/metadata/configuration/service/configuration.service.ts new file mode 100644 index 000000000..9ea95a79a --- /dev/null +++ b/ui/src/app/metadata/configuration/service/configuration.service.ts @@ -0,0 +1,35 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { Metadata } from '../../domain/domain.type'; +import { Wizard } from '../../../wizard/model'; +import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; +import { MetadataProviderEditorTypes } from '../../provider/model'; + +export enum PATHS { + resolver = 'EntityDescriptor', + provider = 'MetadataResolvers' +} + +export const DEFINITIONS = { + resolver: MetadataSourceEditor +}; + +@Injectable() +export class MetadataConfigurationService { + + readonly base = '/api'; + + constructor( + private http: HttpClient + ) {} + + find(id: string, type: string): Observable { + return this.http.get(`${this.base}/${PATHS[type]}/${id}`); + } + + getDefinition(model: Metadata): Wizard { + return MetadataProviderEditorTypes.find(def => def.type === model['@type']) || new MetadataSourceEditor(); + } +} + diff --git a/ui/src/app/metadata/configuration/service/utility.ts b/ui/src/app/metadata/configuration/service/utility.ts new file mode 100644 index 000000000..ee329c3b1 --- /dev/null +++ b/ui/src/app/metadata/configuration/service/utility.ts @@ -0,0 +1,40 @@ +import { Property } from '../../domain/model/property'; + +export function getDefinition(path: string, definitions: any): any { + let def = path.split('/').pop(); + return definitions[def]; +} +export function getPropertyItemSchema(items: any, definitions: any): any { + if (!items) { return null; } + return items.$ref ? getDefinition(items.$ref, definitions) : items; +} + +export function getStepProperty(property, model, definitions): Property { + if (!property) { return null; } + property = property.$ref ? { ...property, ...getDefinition(property.$ref, definitions) } : property; + return { + name: property.title, + value: model, + type: property.type, + items: getPropertyItemSchema(property.items, definitions), + properties: getStepProperties( + property, + model, + definitions + ), + widget: property.widget instanceof String ? { id: property.widget } : { ...property.widget } + }; +} + +export function getStepProperties(schema: any, model: any, definitions: any = {}): Property[] { + if (!schema || !schema.properties) { return []; } + return Object + .keys(schema.properties) + .map(property => { + return getStepProperty( + schema.properties[property], + model && model.hasOwnProperty(property) ? model[property] : null, + definitions + ); + }); +} diff --git a/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts b/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts index cbe98d5de..2c5c66a59 100644 --- a/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts +++ b/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts @@ -2,7 +2,7 @@ import { Wizard, WizardStep } from '../../../../wizard/model'; import { MetadataResolver } from '../metadata-resolver'; import { MetadataSourceBase } from './metadata-source-base'; -export class MetadataSourceEditor extends MetadataSourceBase implements Wizard { +export class MetadataSourceEditor extends MetadataSourceBase implements Wizard { steps: WizardStep[] = [ { index: 1, diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.html b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.html index e2136c4a1..e9aea1c2b 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.html +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.html @@ -24,7 +24,7 @@ [isOpen]="(entitiesOpen$ | async)[resolver.getId()]" (select)="edit(resolver)" (toggle)="toggleEntity(resolver)" - (preview)="openPreviewDialog(resolver)" + (preview)="viewConfiguration(resolver)" (delete)="deleteResolver(resolver)" (history)="viewMetadataHistory(resolver)" [allowDelete]="resolver.isDraft()"> diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts index 540d5b694..0c20310c7 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts @@ -86,8 +86,8 @@ export class DashboardResolversListComponent implements OnInit { this.store.dispatch(new ToggleEntityDisplay(entity.getId())); } - openPreviewDialog(entity: MetadataEntity): void { - this.store.dispatch(new PreviewEntity({ id: entity.getId(), entity })); + viewConfiguration(entity: MetadataEntity): void { + this.router.navigate(['metadata', 'resolver', entity.getId(), 'configuration']); } viewMetadataHistory(entity: MetadataEntity): void { diff --git a/ui/src/app/metadata/metadata.module.ts b/ui/src/app/metadata/metadata.module.ts index 80f663b2a..231f39f5f 100644 --- a/ui/src/app/metadata/metadata.module.ts +++ b/ui/src/app/metadata/metadata.module.ts @@ -12,7 +12,7 @@ import { CustomWidgetRegistry } from '../schema-form/registry'; import { WidgetRegistry, SchemaValidatorFactory } from 'ngx-schema-form'; import { CustomSchemaValidatorFactory } from '../schema-form/service/schema-validator'; import { MetadataVersionModule } from './version/version.module'; - +import { MetadataConfigurationModule } from './configuration/configuration.module'; @NgModule({ imports: [ @@ -22,6 +22,7 @@ import { MetadataVersionModule } from './version/version.module'; ManagerModule.forRoot(), ProviderModule.forRoot(), MetadataVersionModule.forRoot(), + MetadataConfigurationModule.forRoot(), MetadataRoutingModule, I18nModule ], diff --git a/ui/src/app/metadata/metadata.routing.ts b/ui/src/app/metadata/metadata.routing.ts index a1d8964f2..35902415c 100644 --- a/ui/src/app/metadata/metadata.routing.ts +++ b/ui/src/app/metadata/metadata.routing.ts @@ -6,6 +6,7 @@ import { ResolverRoutes } from './resolver/resolver.routing'; import { ProviderRoutes } from './provider/provider.routing'; import { ManagerRoutes } from './manager/manager.routing'; import { VersionRoutes } from './version/version.routing'; +import { ConfigurationRoutes } from './configuration/configuration.routing'; const routes: Routes = [ { @@ -15,7 +16,8 @@ const routes: Routes = [ ...ManagerRoutes, ...ResolverRoutes, ...ProviderRoutes, - ...VersionRoutes + ...VersionRoutes, + ...ConfigurationRoutes ], }, ]; From f94c3934931a2a464657091f67206d9930331f8a Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Mon, 3 Jun 2019 15:31:40 -0700 Subject: [PATCH 02/10] SHIBUI-1267 Implemented configuration component --- .../resources/metadata-sources-ui-schema.json | 1 + .../action/configuration.action.ts | 28 + .../component/array-property.component.html | 51 ++ .../component/array-property.component.ts | 36 + .../configuration-property.component.html | 0 .../configuration-property.component.ts | 19 + .../metadata-configuration.component.html | 33 +- .../metadata-configuration.component.ts | 8 +- .../component/object-property.component.html | 7 + .../component/object-property.component.ts | 26 + .../primitive-property.component.html | 5 + .../component/primitive-property.component.ts | 15 + .../configuration/configuration.module.ts | 16 +- .../container/configuration.component.html | 2 +- .../container/configuration.component.ts | 4 +- .../effect/configuration.effect.ts | 33 +- .../model/metadata-configuration.ts | 2 +- .../metadata/configuration/reducer/index.ts | 58 +- .../service/configuration.service.ts | 5 + ui/src/app/metadata/domain/model/property.ts | 1 + .../model/wizards/metadata-source-base.ts | 1 + .../model/wizards/metadata-source-editor.ts | 9 +- .../model/wizards/metadata-source-wizard.ts | 11 +- .../provider/model/base.provider.form.ts | 1 + .../model/dynamic-http.provider.form.ts | 9 +- .../model/file-backed-http.provider.form.ts | 10 +- .../model/file-system.provider.form.ts | 6 +- .../model/local-dynamic.provider.form.ts | 6 +- .../metadata/provider/model/provider.form.ts | 2 +- .../container/resolver-edit.component.html | 3 +- .../container/resolver-edit.component.ts | 6 - ui/src/app/wizard/model/wizard.ts | 2 +- ui/src/app/wizard/reducer/index.ts | 8 +- .../filebacked-http-advanced.schema.json | 242 ------ .../filebacked-http-common.editor.schema.json | 259 ------- .../filebacked-http-common.schema.json | 173 ----- .../filebacked-http-filters.schema.json | 129 ---- .../filebacked-http-reloading.schema.json | 68 -- .../provider/filebacked-http.schema.json | 686 ++++++++++++++++++ ui/src/styles.scss | 1 + ui/src/theme/utility.scss | 7 + 41 files changed, 1001 insertions(+), 988 deletions(-) create mode 100644 ui/src/app/metadata/configuration/component/array-property.component.html create mode 100644 ui/src/app/metadata/configuration/component/array-property.component.ts create mode 100644 ui/src/app/metadata/configuration/component/configuration-property.component.html create mode 100644 ui/src/app/metadata/configuration/component/configuration-property.component.ts create mode 100644 ui/src/app/metadata/configuration/component/object-property.component.html create mode 100644 ui/src/app/metadata/configuration/component/object-property.component.ts create mode 100644 ui/src/app/metadata/configuration/component/primitive-property.component.html create mode 100644 ui/src/app/metadata/configuration/component/primitive-property.component.ts delete mode 100644 ui/src/assets/schema/provider/filebacked-http-advanced.schema.json delete mode 100644 ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json delete mode 100644 ui/src/assets/schema/provider/filebacked-http-common.schema.json delete mode 100644 ui/src/assets/schema/provider/filebacked-http-filters.schema.json delete mode 100644 ui/src/assets/schema/provider/filebacked-http-reloading.schema.json create mode 100644 ui/src/assets/schema/provider/filebacked-http.schema.json create mode 100644 ui/src/theme/utility.scss diff --git a/backend/src/main/resources/metadata-sources-ui-schema.json b/backend/src/main/resources/metadata-sources-ui-schema.json index 3f002253a..9d64e75b4 100644 --- a/backend/src/main/resources/metadata-sources-ui-schema.json +++ b/backend/src/main/resources/metadata-sources-ui-schema.json @@ -310,6 +310,7 @@ }, "attributeRelease": { "type": "array", + "title": "label.attribute-release", "description": "Attribute release table - select the attributes you want to release (default unchecked)", "widget": { "id": "checklist", diff --git a/ui/src/app/metadata/configuration/action/configuration.action.ts b/ui/src/app/metadata/configuration/action/configuration.action.ts index 42d1dddaa..6c71b61a6 100644 --- a/ui/src/app/metadata/configuration/action/configuration.action.ts +++ b/ui/src/app/metadata/configuration/action/configuration.action.ts @@ -8,6 +8,10 @@ export enum ConfigurationActionTypes { LOAD_METADATA_SUCCESS = '[Metadata Configuration] Load Metadata Success', LOAD_METADATA_ERROR = '[Metadata Configuration] Load Metadata Error', + LOAD_SCHEMA_REQUEST = '[Metadata Configuration] Load Schema Request', + LOAD_SCHEMA_SUCCESS = '[Metadata Configuration] Load Schema Success', + LOAD_SCHEMA_ERROR = '[Metadata Configuration] Load Schema Error', + SET_METADATA = '[Metadata Configuration] Set Metadata Model', SET_DEFINITION = '[Metadata Configuration] Set Metadata Definition', SET_SCHEMA = '[Metadata Configuration] Set Metadata Schema', @@ -32,6 +36,24 @@ export class LoadMetadataError implements Action { constructor(public payload: any) { } } +export class LoadSchemaRequest implements Action { + readonly type = ConfigurationActionTypes.LOAD_SCHEMA_REQUEST; + + constructor(public payload: string) { } +} + +export class LoadSchemaSuccess implements Action { + readonly type = ConfigurationActionTypes.LOAD_SCHEMA_SUCCESS; + + constructor(public payload: Schema) { } +} + +export class LoadSchemaError implements Action { + readonly type = ConfigurationActionTypes.LOAD_SCHEMA_ERROR; + + constructor(public payload: any) { } +} + export class SetMetadata implements Action { readonly type = ConfigurationActionTypes.SET_METADATA; @@ -55,6 +77,12 @@ export class ClearConfiguration implements Action { } export type ConfigurationActionsUnion = + | LoadMetadataRequest + | LoadMetadataSuccess + | LoadMetadataError + | LoadSchemaRequest + | LoadSchemaSuccess + | LoadSchemaError | SetMetadata | SetDefinition | SetSchema diff --git a/ui/src/app/metadata/configuration/component/array-property.component.html b/ui/src/app/metadata/configuration/component/array-property.component.html new file mode 100644 index 000000000..ee177445e --- /dev/null +++ b/ui/src/app/metadata/configuration/component/array-property.component.html @@ -0,0 +1,51 @@ +
+ +
{{ property.name }}
+
+
+
+
+ {{ i + 1 }}.  + {{ property.items.properties[prop].title }} +
+
+ {{ value[prop] }} +
+
+
+
+
+ + + + + + + + +
+ {{ attr.label }} +
+ + true + + + false + +
+
+
+
+
+ +
+ {{ property.name }} +

+
    +
  • + {{ item }} +
  • +
+
+
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/array-property.component.ts b/ui/src/app/metadata/configuration/component/array-property.component.ts new file mode 100644 index 000000000..77c8fcab0 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/array-property.component.ts @@ -0,0 +1,36 @@ +import { Component, Input } from '@angular/core'; +import { Property } from '../../domain/model/property'; +import { Observable, of } from 'rxjs'; +import { AttributesService } from '../../domain/service/attributes.service'; +import { ConfigurationPropertyComponent } from './configuration-property.component'; + +@Component({ + selector: 'array-property', + templateUrl: './array-property.component.html', + styleUrls: [] +}) + +export class ArrayPropertyComponent extends ConfigurationPropertyComponent { + @Input() property: Property; + + constructor( + private attrService: AttributesService + ) { + super(); + } + + getKeys(schema): string[] { + return Object.keys(schema.properties); + } + + get attributeList$(): Observable<{ key: string, label: string }[]> { + if (this.property.widget && this.property.widget.hasOwnProperty('data')) { + return of(this.property.widget.data); + } + if (this.property.widget && this.property.widget.hasOwnProperty('dataUrl')) { + return this.attrService.query(this.property.widget.dataUrl); + } + return of([]); + } +} + diff --git a/ui/src/app/metadata/configuration/component/configuration-property.component.html b/ui/src/app/metadata/configuration/component/configuration-property.component.html new file mode 100644 index 000000000..e69de29bb diff --git a/ui/src/app/metadata/configuration/component/configuration-property.component.ts b/ui/src/app/metadata/configuration/component/configuration-property.component.ts new file mode 100644 index 000000000..91e97e87b --- /dev/null +++ b/ui/src/app/metadata/configuration/component/configuration-property.component.ts @@ -0,0 +1,19 @@ +import { Component, Input } from '@angular/core'; +import { Property } from '../../domain/model/property'; + +@Component({ + selector: 'configuration-property', + templateUrl: './configuration-property.component.html', + styleUrls: [] +}) + +export class ConfigurationPropertyComponent { + @Input() property: Property; + + constructor() { } + + getItemType(items: Property): string { + return items.widget ? items.widget.id : 'default'; + } +} + 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 f2034d06b..30e133525 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html @@ -1,10 +1,23 @@ -
-
-
- {{ i + 1 }}: {{ section.label }} - - {{ prop | json }} - -
-
-
\ No newline at end of file +
+
+
+
+

+ 0{{ i + 1 }} + {{ section.label | translate }} +

+ +
+
+ Option + Value +
+ +
+
+
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 578b7abd3..6b3089f78 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts @@ -1,7 +1,7 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; -import { WizardStep } from '../../../wizard/model'; -import Section from '../model/section'; import { MetadataConfiguration } from '../model/metadata-configuration'; +import { Property } from '../../domain/model/property'; +import Section from '../model/section'; @Component({ selector: 'metadata-configuration', @@ -13,4 +13,8 @@ export class MetadataConfigurationComponent { @Input() configuration: MetadataConfiguration; constructor() { } + + edit(section: Section): void { + console.log(section); + } } diff --git a/ui/src/app/metadata/configuration/component/object-property.component.html b/ui/src/app/metadata/configuration/component/object-property.component.html new file mode 100644 index 000000000..b0aa7b967 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/object-property.component.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ui/src/app/metadata/configuration/component/object-property.component.ts b/ui/src/app/metadata/configuration/component/object-property.component.ts new file mode 100644 index 000000000..db107da92 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/object-property.component.ts @@ -0,0 +1,26 @@ +import { Component, Input } from '@angular/core'; +import { Property } from '../../domain/model/property'; +import { ConfigurationPropertyComponent } from './configuration-property.component'; + +@Component({ + selector: 'object-property', + templateUrl: './object-property.component.html', + styleUrls: [] +}) + +export class ObjectPropertyComponent extends ConfigurationPropertyComponent { + @Input() property: Property; + + constructor() { + super(); + } + + getKeys(schema): string[] { + return Object.keys(schema.properties); + } + + getItemType(items: Property): string { + return items.widget ? items.widget.id : 'default'; + } +} + diff --git a/ui/src/app/metadata/configuration/component/primitive-property.component.html b/ui/src/app/metadata/configuration/component/primitive-property.component.html new file mode 100644 index 000000000..9eef2181f --- /dev/null +++ b/ui/src/app/metadata/configuration/component/primitive-property.component.html @@ -0,0 +1,5 @@ +
+ {{ property.name }} + {{ property.value || property.value === false ? property.value : '-' }} +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/primitive-property.component.ts b/ui/src/app/metadata/configuration/component/primitive-property.component.ts new file mode 100644 index 000000000..951d2c72a --- /dev/null +++ b/ui/src/app/metadata/configuration/component/primitive-property.component.ts @@ -0,0 +1,15 @@ +import { Component, Input } from '@angular/core'; +import { ConfigurationPropertyComponent } from './configuration-property.component'; + +@Component({ + selector: 'primitive-property', + templateUrl: './primitive-property.component.html', + styleUrls: [] +}) + +export class PrimitivePropertyComponent extends ConfigurationPropertyComponent { + constructor() { + super(); + } +} + diff --git a/ui/src/app/metadata/configuration/configuration.module.ts b/ui/src/app/metadata/configuration/configuration.module.ts index c04ab7add..eb69b14af 100644 --- a/ui/src/app/metadata/configuration/configuration.module.ts +++ b/ui/src/app/metadata/configuration/configuration.module.ts @@ -3,22 +3,36 @@ import { CommonModule } from '@angular/common'; import { StoreModule } from '@ngrx/store'; import { EffectsModule } from '@ngrx/effects'; +import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; + import { I18nModule } from '../../i18n/i18n.module'; import { MetadataConfigurationComponent } from './component/metadata-configuration.component'; import { ConfigurationComponent } from './container/configuration.component'; import { MetadataConfigurationService } from './service/configuration.service'; import * as fromConfig from './reducer'; import { MetadataConfigurationEffects } from './effect/configuration.effect'; +import { ConfigurationPropertyComponent } from './component/configuration-property.component'; +import { DomainModule } from '../domain/domain.module'; +import { PrimitivePropertyComponent } from './component/primitive-property.component'; +import { ObjectPropertyComponent } from './component/object-property.component'; +import { ArrayPropertyComponent } from './component/array-property.component'; +import { RouterModule } from '@angular/router'; @NgModule({ declarations: [ MetadataConfigurationComponent, + ConfigurationPropertyComponent, + PrimitivePropertyComponent, + ObjectPropertyComponent, + ArrayPropertyComponent, ConfigurationComponent ], entryComponents: [], imports: [ CommonModule, - I18nModule + I18nModule, + NgbPopoverModule, + RouterModule ], exports: [], providers: [] diff --git a/ui/src/app/metadata/configuration/container/configuration.component.html b/ui/src/app/metadata/configuration/container/configuration.component.html index 21e8a3311..1e9f67486 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.html +++ b/ui/src/app/metadata/configuration/container/configuration.component.html @@ -11,7 +11,7 @@
- +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts index 3c47e0b93..eb52f60ba 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.ts @@ -24,7 +24,7 @@ export class ConfigurationComponent implements OnDestroy { private store: Store, private routerState: ActivatedRoute ) { - this.configuration$ = this.store.select(fromConfiguration.getConfigurationColumns); + this.configuration$ = this.store.select(fromConfiguration.getConfigurationSections); this.routerState.params.pipe( takeUntil(this.ngUnsubscribe), @@ -32,8 +32,6 @@ export class ConfigurationComponent implements OnDestroy { ).subscribe(store); this.configuration$.subscribe(c => console.log(c)); - - // this.resolver$ = this.store.select(fromResolvers.getSelectedResolver).pipe(skipWhile(p => !p)); } ngOnDestroy() { diff --git a/ui/src/app/metadata/configuration/effect/configuration.effect.ts b/ui/src/app/metadata/configuration/effect/configuration.effect.ts index b3c8f0515..ef86d13fd 100644 --- a/ui/src/app/metadata/configuration/effect/configuration.effect.ts +++ b/ui/src/app/metadata/configuration/effect/configuration.effect.ts @@ -10,7 +10,11 @@ import { LoadMetadataError, ConfigurationActionTypes, SetMetadata, - SetDefinition + SetDefinition, + LoadSchemaRequest, + LoadSchemaSuccess, + SetSchema, + LoadSchemaError } from '../action/configuration.action'; @Injectable() @@ -36,11 +40,36 @@ export class MetadataConfigurationEffects { ); @Effect() - setDefinitionOnMetadataSet$ = this.actions$.pipe( + setDefinition$ = this.actions$.pipe( ofType(ConfigurationActionTypes.SET_METADATA), map(action => new SetDefinition(this.configService.getDefinition(action.payload))) ); + @Effect() + loadSchemaOnDefinitionSet$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.SET_DEFINITION), + map(action => new LoadSchemaRequest(action.payload.schema)) + ); + + @Effect() + loadSchemaData$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.LOAD_SCHEMA_REQUEST), + switchMap(action => + this.configService + .loadSchema(action.payload) + .pipe( + map(schema => new LoadSchemaSuccess(schema)), + catchError(error => of(new LoadSchemaError(error))) + ) + ) + ); + + @Effect() + setSchema$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.LOAD_SCHEMA_SUCCESS), + map(action => new SetSchema(action.payload)) + ); + constructor( private configService: MetadataConfigurationService, private actions$: Actions diff --git a/ui/src/app/metadata/configuration/model/metadata-configuration.ts b/ui/src/app/metadata/configuration/model/metadata-configuration.ts index 10768f14e..b8a37b85e 100644 --- a/ui/src/app/metadata/configuration/model/metadata-configuration.ts +++ b/ui/src/app/metadata/configuration/model/metadata-configuration.ts @@ -1,5 +1,5 @@ import Section from './section'; export interface MetadataConfiguration { - columns: Array
[]; + sections: Section[]; } diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index 13b5c627f..3fb2ae27d 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -29,47 +29,29 @@ export const getConfigurationModel = createSelector(getConfigurationState, fromC export const getConfigurationDefinition = createSelector(getConfigurationState, fromConfiguration.getDefinition); export const getConfigurationSchema = createSelector(getConfigurationState, fromConfiguration.getSchema); -export const mergedSchema = createSelector(getConfigurationSchema, schema => !schema ? null : Object.keys(schema).reduce((coll, key) => ({ - ...merge(coll, schema[key]) -}), {} as any)); - export const getConfigurationSectionsFn = (model, definition, schema) => !definition || !schema ? null : - 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(model), - schema.definitions || {} - ) - }); - } - ); - -export const getConfigurationColumnsFn = sections => !sections ? null : - 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; - }, []); - + ({ + 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(model), + schema.definitions || {} + ) + }); + } + ) + }); export const getConfigurationSections = createSelector( getConfigurationModel, getConfigurationDefinition, - mergedSchema, + getConfigurationSchema, getConfigurationSectionsFn ); - -export const getConfigurationColumns = createSelector(getConfigurationSections, getConfigurationColumnsFn); diff --git a/ui/src/app/metadata/configuration/service/configuration.service.ts b/ui/src/app/metadata/configuration/service/configuration.service.ts index 9ea95a79a..d7f5641d9 100644 --- a/ui/src/app/metadata/configuration/service/configuration.service.ts +++ b/ui/src/app/metadata/configuration/service/configuration.service.ts @@ -5,6 +5,7 @@ import { Metadata } from '../../domain/domain.type'; import { Wizard } from '../../../wizard/model'; import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; import { MetadataProviderEditorTypes } from '../../provider/model'; +import { Schema } from '../model/schema'; export enum PATHS { resolver = 'EntityDescriptor', @@ -31,5 +32,9 @@ export class MetadataConfigurationService { getDefinition(model: Metadata): Wizard { return MetadataProviderEditorTypes.find(def => def.type === model['@type']) || new MetadataSourceEditor(); } + + loadSchema(path: string): Observable { + return this.http.get(path); + } } diff --git a/ui/src/app/metadata/domain/model/property.ts b/ui/src/app/metadata/domain/model/property.ts index f54829916..a792514d8 100644 --- a/ui/src/app/metadata/domain/model/property.ts +++ b/ui/src/app/metadata/domain/model/property.ts @@ -1,4 +1,5 @@ export interface Property { + title?: string; type: string; name: string; value: string[]; diff --git a/ui/src/app/metadata/domain/model/wizards/metadata-source-base.ts b/ui/src/app/metadata/domain/model/wizards/metadata-source-base.ts index 673f176c1..aab844d5e 100644 --- a/ui/src/app/metadata/domain/model/wizards/metadata-source-base.ts +++ b/ui/src/app/metadata/domain/model/wizards/metadata-source-base.ts @@ -9,6 +9,7 @@ export class MetadataSourceBase implements Wizard { label = 'Metadata Source'; type = '@MetadataProvider'; steps: WizardStep[] = []; + schema = ''; bindings = { '/securityInfo/x509CertificateAvailable': [ diff --git a/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts b/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts index 2c5c66a59..13136ddfc 100644 --- a/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts +++ b/ui/src/app/metadata/domain/model/wizards/metadata-source-editor.ts @@ -3,12 +3,12 @@ import { MetadataResolver } from '../metadata-resolver'; import { MetadataSourceBase } from './metadata-source-base'; export class MetadataSourceEditor extends MetadataSourceBase implements Wizard { + schema = '/api/ui/MetadataSources'; steps: WizardStep[] = [ { index: 1, id: 'common', label: 'label.sp-org-info', - schema: '/api/ui/MetadataSources', fields: [ 'serviceProviderName', 'entityId', @@ -38,7 +38,6 @@ export class MetadataSourceEditor extends MetadataSourceBase implements Wizard { + schema = '/api/ui/MetadataSources'; steps: WizardStep[] = [ { index: 1, id: 'common', label: 'label.name-and-entity-id', - schema: '/api/ui/MetadataSources', fields: [ 'serviceProviderName', 'entityId' @@ -28,7 +28,6 @@ export class MetadataSourceWizard extends MetadataSourceBase implements Wizard = { label: 'BaseMetadataProvider', type: 'BaseMetadataResolver', + schema: '', getValidators(namesList: string[]): any { const validators = { '/': (value, property, form_current) => { diff --git a/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts index be0654dec..a0ca22143 100644 --- a/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts +++ b/ui/src/app/metadata/provider/model/dynamic-http.provider.form.ts @@ -80,13 +80,13 @@ export const DynamicHttpMetadataProviderWizard: Wizard = { ...FileBackedHttpMetadataProviderWizard, + schema: 'assets/schema/provider/filebacked-http.schema.json', steps: [ { id: 'common', label: 'label.common-attributes', index: 1, initialValues: [], - schema: 'assets/schema/provider/filebacked-http-common.editor.schema.json', fields: [ 'enabled', 'xmlId', @@ -120,7 +117,6 @@ export const FileBackedHttpMetadataProviderEditor: Wizard = { ...BaseMetadataProviderEditor, label: 'MetadataProvider', type: 'MetadataProvider', + schema: 'assets/schema/provider/metadata-provider.schema.json', steps: [ { id: 'new', label: 'label.select-metadata-provider-type', index: 1, initialValues: [], - schema: 'assets/schema/provider/metadata-provider.schema.json', fields: [ 'name', '@type' diff --git a/ui/src/app/metadata/resolver/container/resolver-edit.component.html b/ui/src/app/metadata/resolver/container/resolver-edit.component.html index bf04c16ae..88eb1946a 100644 --- a/ui/src/app/metadata/resolver/container/resolver-edit.component.html +++ b/ui/src/app/metadata/resolver/container/resolver-edit.component.html @@ -31,7 +31,8 @@   diff --git a/ui/src/app/metadata/resolver/container/resolver-edit.component.ts b/ui/src/app/metadata/resolver/container/resolver-edit.component.ts index 6161919f9..f793ef96e 100644 --- a/ui/src/app/metadata/resolver/container/resolver-edit.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-edit.component.ts @@ -56,8 +56,6 @@ export class ResolverEditComponent implements OnDestroy, CanComponentDeactivate let startIndex$ = this.route.firstChild.params.pipe(map(p => p.form)); startIndex$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(index => this.store.dispatch(new SetIndex(index))); - this.index$.pipe(takeUntil(this.ngUnsubscribe)).subscribe(index => index && this.go(index)); - this.store .select(fromWizard.getCurrentWizardSchema) .pipe(filter(s => !!s)) @@ -67,10 +65,6 @@ export class ResolverEditComponent implements OnDestroy, CanComponentDeactivate this.store.select(fromResolver.getEntityChanges).subscribe(changes => this.latest = changes); } - go(index: string): void { - this.router.navigate(['./', index], { relativeTo: this.route }); - } - ngOnDestroy() { this.clear(); this.ngUnsubscribe.next(); diff --git a/ui/src/app/wizard/model/wizard.ts b/ui/src/app/wizard/model/wizard.ts index 3115ed041..5729c4f24 100644 --- a/ui/src/app/wizard/model/wizard.ts +++ b/ui/src/app/wizard/model/wizard.ts @@ -2,13 +2,13 @@ import { FormDefinition } from './form-definition'; export interface Wizard extends FormDefinition { steps: WizardStep[]; + schema: string; } export interface WizardStep { id: string; label: string; initialValues?: WizardValue[]; - schema?: string; index: number; locked?: boolean; fields?: string[]; diff --git a/ui/src/app/wizard/reducer/index.ts b/ui/src/app/wizard/reducer/index.ts index 3446f3713..4c3321c62 100644 --- a/ui/src/app/wizard/reducer/index.ts +++ b/ui/src/app/wizard/reducer/index.ts @@ -48,11 +48,7 @@ export const getWizardIsDisabled = createSelector(getState, fromWizard.getDisabl export const getWizardDefinition = createSelector(getState, fromWizard.getDefinition); export const getSchemaCollection = createSelector(getState, fromWizard.getCollection); -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 getSchemaPath = (wizard: Wizard) => wizard ? wizard.schema : null; export const getSplitSchema = (schema: any, step: WizardStep) => { if (!schema || !step.fields || !step.fields.length || !schema.properties) { @@ -93,7 +89,7 @@ export const getSplitSchema = (schema: any, step: WizardStep) => { return s; }; -export const getCurrentWizardSchema = createSelector(getWizardIndex, getWizardDefinition, getSchemaPath); +export const getCurrentWizardSchema = createSelector(getWizardDefinition, getSchemaPath); export const getPreviousFn = (index: string, wizard: Wizard) => { if (!wizard) { return null; } diff --git a/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json b/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json deleted file mode 100644 index 2e19da247..000000000 --- a/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json +++ /dev/null @@ -1,242 +0,0 @@ -{ - "type": "object", - "title": "", - "properties": { - "httpMetadataResolverAttributes": { - "order": [], - "type": "object", - "fieldsets": [ - { - "title": "label.http-connection-attributes", - "type": "section", - "fields": [ - "connectionRequestTimeout", - "connectionTimeout", - "socketTimeout" - ] - }, - { - "title": "label.http-security-attributes", - "type": "section", - "class": "col-12", - "fields": [ - "disregardTLSCertificate" - ] - }, - { - "title": "label.http-proxy-attributes", - "type": "section", - "class": "col-12", - "fields": [ - "proxyHost", - "proxyPort", - "proxyUser", - "proxyPassword" - ] - }, - { - "title": "label.http-caching-attributes", - "type": "section", - "class": "col-12", - "fields": [ - "httpCaching", - "httpCacheDirectory", - "httpMaxCacheEntries", - "httpMaxCacheEntrySize" - ] - }, - { - "title": "", - "type": "hidden", - "class": "col-12", - "fields": [ - "tlsTrustEngineRef", - "httpClientSecurityParametersRef", - "httpClientRef" - ] - } - ], - "properties": { - "httpClientRef": { - "type": "string", - "title": "", - "description": "", - "placeholder": "", - "widget": "hidden" - }, - "connectionRequestTimeout": { - "type": "string", - "title": "label.connection-request-timeout", - "description": "tooltip.connection-request-timeout", - "placeholder": "label.duration", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "PT30S", - "PT1M", - "PT10M", - "PT30M", - "PT1H", - "PT4H", - "PT12H", - "PT24H" - ] - }, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - }, - "connectionTimeout": { - "type": "string", - "title": "label.connection-timeout", - "description": "tooltip.connection-timeout", - "placeholder": "label.duration", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "PT30S", - "PT1M", - "PT10M", - "PT30M", - "PT1H", - "PT4H", - "PT12H", - "PT24H" - ] - }, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - }, - "socketTimeout": { - "type": "string", - "title": "label.socket-timeout", - "description": "tooltip.socket-timeout", - "placeholder": "label.duration", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "PT30S", - "PT1M", - "PT10M", - "PT30M", - "PT1H", - "PT4H", - "PT12H", - "PT24H" - ] - }, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - }, - "disregardTLSCertificate": { - "type": "boolean", - "title": "label.disregard-tls-cert", - "description": "tooltip.disregard-tls-cert", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "True" - }, - { - "enum": [ - false - ], - "description": "False" - } - ] - }, - "tlsTrustEngineRef": { - "type": "string", - "title": "", - "description": "", - "placeholder": "", - "widget": "hidden" - }, - "httpClientSecurityParametersRef": { - "type": "string", - "title": "", - "description": "", - "placeholder": "", - "widget": "hidden" - }, - "proxyHost": { - "type": "string", - "title": "label.proxy-host", - "description": "tooltip.proxy-host", - "placeholder": "" - }, - "proxyPort": { - "type": "string", - "title": "label.proxy-port", - "description": "tooltip.proxy-port", - "placeholder": "" - }, - "proxyUser": { - "type": "string", - "title": "label.proxy-user", - "description": "tooltip.proxy-user", - "placeholder": "" - }, - "proxyPassword": { - "type": "string", - "title": "label.proxy-password", - "description": "tooltip.proxy-password", - "placeholder": "" - }, - "httpCaching": { - "type": "string", - "title": "label.http-caching", - "description": "tooltip.http-caching", - "placeholder": "label.select-caching-type", - "widget": { - "id": "select" - }, - "oneOf": [ - { - "enum": [ - "none" - ], - "description": "value.none" - }, - { - "enum": [ - "file" - ], - "description": "value.file" - }, - { - "enum": [ - "memory" - ], - "description": "value.memory" - } - ] - }, - "httpCacheDirectory": { - "type": "string", - "title": "label.http-caching-directory", - "description": "tooltip.http-caching-directory", - "placeholder": "" - }, - "httpMaxCacheEntries": { - "type": "integer", - "title": "label.http-max-cache-entries", - "description": "tooltip.http-max-cache-entries", - "placeholder": "", - "minimum": 0 - }, - "httpMaxCacheEntrySize": { - "type": "integer", - "title": "label.max-cache-entry-size", - "description": "tooltip.max-cache-entry-size", - "placeholder": "", - "minimum": 0 - } - } - } - } -} diff --git a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json deleted file mode 100644 index 9e1d37308..000000000 --- a/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json +++ /dev/null @@ -1,259 +0,0 @@ -{ - "type": "object", - "order": [ - "name", - "@type", - "xmlId", - "metadataURL", - "initializeFromBackupFile", - "backingFile", - "backupFileInitNextRefreshDelay", - "requireValidMetadata", - "failFastInitialization", - "useDefaultPredicateRegistry", - "satisfyAnyPredicates" - ], - "required": [ - "name", - "xmlId", - "metadataURL", - "backingFile", - "backupFileInitNextRefreshDelay" - ], - "anyOf": [ - { - "properties": { - "initializeFromBackupFile": { - "enum": [ - true - ] - } - } - }, - { - "properties": { - "initializeFromBackupFile": { - "enum": [ - false - ] - } - } - } - ], - "fieldsets": [ - { - "type": "section", - "fields": [ - "name", - "@type", - "enabled" - ] - }, - { - "type": "group-lg", - "fields": [ - "xmlId", - "metadataURL", - "initializeFromBackupFile", - "backingFile", - "backupFileInitNextRefreshDelay", - "requireValidMetadata", - "failFastInitialization", - "useDefaultPredicateRegistry", - "satisfyAnyPredicates" - ] - } - ], - "properties": { - "name": { - "title": "label.metadata-provider-name", - "description": "tooltip.metadata-provider-name", - "type": "string", - "widget": { - "id": "string", - "help": "message.must-be-unique" - } - }, - "@type": { - "title": "label.metadata-provider-type", - "description": "tooltip.metadata-provider-type", - "placeholder": "label.select-metadata-type", - "type": "string", - "readOnly": true, - "widget": { - "id": "select", - "disabled": true - }, - "oneOf": [ - { - "enum": [ - "FileBackedHttpMetadataResolver" - ], - "description": "value.file-backed-http-metadata-provider" - } - ] - }, - "enabled": { - "title": "label.enable-service", - "description": "tooltip.enable-service", - "type": "boolean", - "default": false - }, - "xmlId": { - "title": "label.xml-id", - "description": "tooltip.xml-id", - "type": "string", - "default": "", - "minLength": 1 - }, - "metadataURL": { - "title": "label.metadata-url", - "description": "tooltip.metadata-url", - "type": "string", - "default": "", - "minLength": 1 - }, - "initializeFromBackupFile": { - "title": "label.init-from-backup", - "description": "tooltip.init-from-backup", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "backingFile": { - "title": "label.backing-file", - "description": "tooltip.backing-file", - "type": "string", - "default": "" - }, - "backupFileInitNextRefreshDelay": { - "title": "label.backup-file-init-refresh-delay", - "description": "tooltip.backup-file-init-refresh-delay", - "type": "string", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "PT30S", - "PT1M", - "PT10M", - "PT30M", - "PT1H", - "PT4H", - "PT12H", - "PT24H" - ] - }, - "default": null, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - }, - "requireValidMetadata": { - "title": "label.require-valid-metadata", - "description": "tooltip.require-valid-metadata", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "failFastInitialization": { - "title": "label.fail-fast-init", - "description": "tooltip.fail-fast-init", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "useDefaultPredicateRegistry": { - "title": "label.use-default-predicate-reg", - "description": "tooltip.use-default-predicate-reg", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "satisfyAnyPredicates": { - "title": "label.satisfy-any-predicates", - "description": "tooltip.satisfy-any-predicates", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": false - } - } -} \ No newline at end of file diff --git a/ui/src/assets/schema/provider/filebacked-http-common.schema.json b/ui/src/assets/schema/provider/filebacked-http-common.schema.json deleted file mode 100644 index 5db366d07..000000000 --- a/ui/src/assets/schema/provider/filebacked-http-common.schema.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "type": "object", - "order": [ - "xmlId", - "metadataURL", - "initializeFromBackupFile", - "backingFile", - "backupFileInitNextRefreshDelay", - "requireValidMetadata", - "failFastInitialization", - "useDefaultPredicateRegistry", - "satisfyAnyPredicates" - ], - "required": ["xmlId", "metadataURL", "backingFile", "backupFileInitNextRefreshDelay"], - "properties": { - "xmlId": { - "title": "label.xml-id", - "description": "tooltip.xml-id", - "type": "string", - "default": "", - "minLength": 1 - }, - "metadataURL": { - "title": "label.metadata-url", - "description": "tooltip.metadata-url", - "type": "string", - "default": "", - "minLength": 1 - }, - "initializeFromBackupFile": { - "title": "label.init-from-backup", - "description": "tooltip.init-from-backup", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "backingFile": { - "title": "label.backing-file", - "description": "tooltip.backing-file", - "type": "string", - "default": "" - }, - "backupFileInitNextRefreshDelay": { - "title": "label.backup-file-init-refresh-delay", - "description": "tooltip.backup-file-init-refresh-delay", - "type": "string", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "PT30S", - "PT1M", - "PT10M", - "PT30M", - "PT1H", - "PT4H", - "PT12H", - "PT24H" - ] - }, - "default": null, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - }, - "requireValidMetadata": { - "title": "label.require-valid-metadata", - "description": "tooltip.require-valid-metadata", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "failFastInitialization": { - "title": "label.fail-fast-init", - "description": "tooltip.fail-fast-init", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "useDefaultPredicateRegistry": { - "title": "label.use-default-predicate-reg", - "description": "tooltip.use-default-predicate-reg", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": true - }, - "satisfyAnyPredicates": { - "title": "label.satisfy-any-predicates", - "description": "tooltip.satisfy-any-predicates", - "type": "boolean", - "widget": { - "id": "boolean-radio" - }, - "oneOf": [ - { - "enum": [ - true - ], - "description": "value.true" - }, - { - "enum": [ - false - ], - "description": "value.false" - } - ], - "default": false - } - } -} diff --git a/ui/src/assets/schema/provider/filebacked-http-filters.schema.json b/ui/src/assets/schema/provider/filebacked-http-filters.schema.json deleted file mode 100644 index 820063bfa..000000000 --- a/ui/src/assets/schema/provider/filebacked-http-filters.schema.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "type": "object", - "properties": { - "metadataFilters": { - "title": "", - "description": "", - "type": "object", - "properties": { - "RequiredValidUntil": { - "title": "label.required-valid-until", - "type": "object", - "widget": { - "id": "fieldset" - }, - "properties": { - "maxValidityInterval": { - "title": "label.max-validity-interval", - "description": "tooltip.max-validity-interval", - "type": "string", - "placeholder": "label.duration", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "P14D", - "P7D", - "P1D", - "PT12H" - ] - }, - "default": null, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - } - } - }, - "SignatureValidation": { - "title": "label.signature-validation-filter", - "type": "object", - "widget": { - "id": "fieldset" - }, - "properties": { - "requireSignedRoot": { - "title": "label.require-signed-root", - "description": "tooltip.require-signed-root", - "type": "boolean", - "default": true - }, - "certificateFile": { - "title": "label.certificate-file", - "description": "tooltip.certificate-file", - "type": "string", - "widget": "textline" - } - }, - "anyOf": [ - { - "properties": { - "requireSignedRoot": { - "enum": [ true ] - }, - "certificateFile": { - "minLength": 1, - "type": "string" - } - }, - "required": [ - "certificateFile" - ] - }, - { - "properties": { - "requireSignedRoot": { - "enum": [ false ] - } - } - } - ] - }, - "EntityRoleWhiteList": { - "title": "label.entity-role-whitelist", - "type": "object", - "widget": { - "id": "fieldset" - }, - "properties": { - "retainedRoles": { - "title": "label.retained-roles", - "description": "tooltip.retained-roles", - "type": "array", - "items": { - "widget": { - "id": "select" - }, - "type": "string", - "oneOf": [ - { - "enum": [ - "md:SPSSODescriptor" - ], - "description": "value.spdescriptor" - }, - { - "enum": [ - "md:AttributeAuthorityDescriptor" - ], - "description": "value.attr-auth-descriptor" - } - ] - } - }, - "removeRolelessEntityDescriptors": { - "title": "label.remove-roleless-entity-descriptors", - "description": "tooltip.remove-roleless-entity-descriptors", - "type": "boolean", - "default": true - }, - "removeEmptyEntitiesDescriptors": { - "title": "label.remove-empty-entities-descriptors", - "description": "tooltip.remove-empty-entities-descriptors", - "type": "boolean", - "default": true - } - } - } - } - } - } -} diff --git a/ui/src/assets/schema/provider/filebacked-http-reloading.schema.json b/ui/src/assets/schema/provider/filebacked-http-reloading.schema.json deleted file mode 100644 index 6723e4cb1..000000000 --- a/ui/src/assets/schema/provider/filebacked-http-reloading.schema.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "type": "object", - "properties": { - "reloadableMetadataResolverAttributes": { - "type": "object", - "properties": { - "minRefreshDelay": { - "title": "label.min-refresh-delay", - "description": "tooltip.min-refresh-delay", - "type": "string", - "placeholder": "label.duration", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "PT30S", - "PT1M", - "PT10M", - "PT30M", - "PT1H", - "PT4H", - "PT12H", - "PT24H" - ] - }, - "default": null, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - }, - "maxRefreshDelay": { - "title": "label.max-refresh-delay", - "description": "tooltip.max-refresh-delay", - "type": "string", - "placeholder": "label.duration", - "widget": { - "id": "datalist", - "data": [ - "PT0S", - "PT30S", - "PT1M", - "PT10M", - "PT30M", - "PT1H", - "PT4H", - "PT12H", - "PT24H" - ] - }, - "default": null, - "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" - }, - "refreshDelayFactor": { - "title": "label.refresh-delay-factor", - "description": "tooltip.refresh-delay-factor", - "type": "string", - "widget": { - "id": "string", - "help": "message.real-number" - }, - "placeholder": "label.real-number", - "minimum": 0, - "maximum": 1, - "default": "", - "pattern": "^(?:([0]*(\\.[0-9]+)?|[0]*\\.[0-9]*[1-9][0-9]*)|)$" - } - } - } - } -} diff --git a/ui/src/assets/schema/provider/filebacked-http.schema.json b/ui/src/assets/schema/provider/filebacked-http.schema.json new file mode 100644 index 000000000..f543e9e89 --- /dev/null +++ b/ui/src/assets/schema/provider/filebacked-http.schema.json @@ -0,0 +1,686 @@ +{ + "type": "object", + "order": [ + "name", + "@type", + "xmlId", + "metadataURL", + "initializeFromBackupFile", + "backingFile", + "backupFileInitNextRefreshDelay", + "requireValidMetadata", + "failFastInitialization", + "useDefaultPredicateRegistry", + "satisfyAnyPredicates" + ], + "required": [ + "name", + "xmlId", + "metadataURL", + "backingFile", + "backupFileInitNextRefreshDelay" + ], + "anyOf": [ + { + "properties": { + "initializeFromBackupFile": { + "enum": [ + true + ] + } + } + }, + { + "properties": { + "initializeFromBackupFile": { + "enum": [ + false + ] + } + } + } + ], + "fieldsets": [ + { + "type": "section", + "fields": [ + "name", + "@type", + "enabled" + ] + }, + { + "type": "group-lg", + "fields": [ + "xmlId", + "metadataURL", + "initializeFromBackupFile", + "backingFile", + "backupFileInitNextRefreshDelay", + "requireValidMetadata", + "failFastInitialization", + "useDefaultPredicateRegistry", + "satisfyAnyPredicates" + ] + } + ], + "properties": { + "name": { + "title": "label.metadata-provider-name", + "description": "tooltip.metadata-provider-name", + "type": "string", + "widget": { + "id": "string", + "help": "message.must-be-unique" + } + }, + "@type": { + "title": "label.metadata-provider-type", + "description": "tooltip.metadata-provider-type", + "placeholder": "label.select-metadata-type", + "type": "string", + "readOnly": true, + "widget": { + "id": "select", + "disabled": true + }, + "oneOf": [ + { + "enum": [ + "FileBackedHttpMetadataResolver" + ], + "description": "value.file-backed-http-metadata-provider" + } + ] + }, + "enabled": { + "title": "label.enable-service", + "description": "tooltip.enable-service", + "type": "boolean", + "default": false + }, + "xmlId": { + "title": "label.xml-id", + "description": "tooltip.xml-id", + "type": "string", + "default": "", + "minLength": 1 + }, + "metadataURL": { + "title": "label.metadata-url", + "description": "tooltip.metadata-url", + "type": "string", + "default": "", + "minLength": 1 + }, + "initializeFromBackupFile": { + "title": "label.init-from-backup", + "description": "tooltip.init-from-backup", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, + "backingFile": { + "title": "label.backing-file", + "description": "tooltip.backing-file", + "type": "string", + "default": "" + }, + "backupFileInitNextRefreshDelay": { + "title": "label.backup-file-init-refresh-delay", + "description": "tooltip.backup-file-init-refresh-delay", + "type": "string", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "requireValidMetadata": { + "title": "label.require-valid-metadata", + "description": "tooltip.require-valid-metadata", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, + "failFastInitialization": { + "title": "label.fail-fast-init", + "description": "tooltip.fail-fast-init", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, + "useDefaultPredicateRegistry": { + "title": "label.use-default-predicate-reg", + "description": "tooltip.use-default-predicate-reg", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": true + }, + "satisfyAnyPredicates": { + "title": "label.satisfy-any-predicates", + "description": "tooltip.satisfy-any-predicates", + "type": "boolean", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "value.true" + }, + { + "enum": [ + false + ], + "description": "value.false" + } + ], + "default": false + }, + "httpMetadataResolverAttributes": { + "order": [], + "type": "object", + "fieldsets": [ + { + "title": "label.http-connection-attributes", + "type": "section", + "fields": [ + "connectionRequestTimeout", + "connectionTimeout", + "socketTimeout" + ] + }, + { + "title": "label.http-security-attributes", + "type": "section", + "class": "col-12", + "fields": [ + "disregardTLSCertificate" + ] + }, + { + "title": "label.http-proxy-attributes", + "type": "section", + "class": "col-12", + "fields": [ + "proxyHost", + "proxyPort", + "proxyUser", + "proxyPassword" + ] + }, + { + "title": "label.http-caching-attributes", + "type": "section", + "class": "col-12", + "fields": [ + "httpCaching", + "httpCacheDirectory", + "httpMaxCacheEntries", + "httpMaxCacheEntrySize" + ] + }, + { + "title": "", + "type": "hidden", + "class": "col-12", + "fields": [ + "tlsTrustEngineRef", + "httpClientSecurityParametersRef", + "httpClientRef" + ] + } + ], + "properties": { + "httpClientRef": { + "type": "string", + "title": "", + "description": "", + "placeholder": "", + "widget": "hidden" + }, + "connectionRequestTimeout": { + "type": "string", + "title": "label.connection-request-timeout", + "description": "tooltip.connection-request-timeout", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "connectionTimeout": { + "type": "string", + "title": "label.connection-timeout", + "description": "tooltip.connection-timeout", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "socketTimeout": { + "type": "string", + "title": "label.socket-timeout", + "description": "tooltip.socket-timeout", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "disregardTLSCertificate": { + "type": "boolean", + "title": "label.disregard-tls-cert", + "description": "tooltip.disregard-tls-cert", + "widget": { + "id": "boolean-radio" + }, + "oneOf": [ + { + "enum": [ + true + ], + "description": "True" + }, + { + "enum": [ + false + ], + "description": "False" + } + ] + }, + "tlsTrustEngineRef": { + "type": "string", + "title": "", + "description": "", + "placeholder": "", + "widget": "hidden" + }, + "httpClientSecurityParametersRef": { + "type": "string", + "title": "", + "description": "", + "placeholder": "", + "widget": "hidden" + }, + "proxyHost": { + "type": "string", + "title": "label.proxy-host", + "description": "tooltip.proxy-host", + "placeholder": "" + }, + "proxyPort": { + "type": "string", + "title": "label.proxy-port", + "description": "tooltip.proxy-port", + "placeholder": "" + }, + "proxyUser": { + "type": "string", + "title": "label.proxy-user", + "description": "tooltip.proxy-user", + "placeholder": "" + }, + "proxyPassword": { + "type": "string", + "title": "label.proxy-password", + "description": "tooltip.proxy-password", + "placeholder": "" + }, + "httpCaching": { + "type": "string", + "title": "label.http-caching", + "description": "tooltip.http-caching", + "placeholder": "label.select-caching-type", + "widget": { + "id": "select" + }, + "oneOf": [ + { + "enum": [ + "none" + ], + "description": "value.none" + }, + { + "enum": [ + "file" + ], + "description": "value.file" + }, + { + "enum": [ + "memory" + ], + "description": "value.memory" + } + ] + }, + "httpCacheDirectory": { + "type": "string", + "title": "label.http-caching-directory", + "description": "tooltip.http-caching-directory", + "placeholder": "" + }, + "httpMaxCacheEntries": { + "type": "integer", + "title": "label.http-max-cache-entries", + "description": "tooltip.http-max-cache-entries", + "placeholder": "", + "minimum": 0 + }, + "httpMaxCacheEntrySize": { + "type": "integer", + "title": "label.max-cache-entry-size", + "description": "tooltip.max-cache-entry-size", + "placeholder": "", + "minimum": 0 + } + } + }, + "reloadableMetadataResolverAttributes": { + "type": "object", + "properties": { + "minRefreshDelay": { + "title": "label.min-refresh-delay", + "description": "tooltip.min-refresh-delay", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "maxRefreshDelay": { + "title": "label.max-refresh-delay", + "description": "tooltip.max-refresh-delay", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "PT30S", + "PT1M", + "PT10M", + "PT30M", + "PT1H", + "PT4H", + "PT12H", + "PT24H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + }, + "refreshDelayFactor": { + "title": "label.refresh-delay-factor", + "description": "tooltip.refresh-delay-factor", + "type": "string", + "widget": { + "id": "string", + "help": "message.real-number" + }, + "placeholder": "label.real-number", + "minimum": 0, + "maximum": 1, + "default": "", + "pattern": "^(?:([0]*(\\.[0-9]+)?|[0]*\\.[0-9]*[1-9][0-9]*)|)$" + } + } + }, + "metadataFilters": { + "title": "", + "description": "", + "type": "object", + "properties": { + "RequiredValidUntil": { + "title": "label.required-valid-until", + "type": "object", + "widget": { + "id": "fieldset" + }, + "properties": { + "maxValidityInterval": { + "title": "label.max-validity-interval", + "description": "tooltip.max-validity-interval", + "type": "string", + "placeholder": "label.duration", + "widget": { + "id": "datalist", + "data": [ + "PT0S", + "P14D", + "P7D", + "P1D", + "PT12H" + ] + }, + "default": null, + "pattern": "^(R\\d*\\/)?P(?:\\d+(?:\\.\\d+)?Y)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?W)?(?:\\d+(?:\\.\\d+)?D)?(?:T(?:\\d+(?:\\.\\d+)?H)?(?:\\d+(?:\\.\\d+)?M)?(?:\\d+(?:\\.\\d+)?S)?)?$" + } + } + }, + "SignatureValidation": { + "title": "label.signature-validation-filter", + "type": "object", + "widget": { + "id": "fieldset" + }, + "properties": { + "requireSignedRoot": { + "title": "label.require-signed-root", + "description": "tooltip.require-signed-root", + "type": "boolean", + "default": true + }, + "certificateFile": { + "title": "label.certificate-file", + "description": "tooltip.certificate-file", + "type": "string", + "widget": "textline" + } + }, + "anyOf": [ + { + "properties": { + "requireSignedRoot": { + "enum": [ + true + ] + }, + "certificateFile": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "certificateFile" + ] + }, + { + "properties": { + "requireSignedRoot": { + "enum": [ + false + ] + } + } + } + ] + }, + "EntityRoleWhiteList": { + "title": "label.entity-role-whitelist", + "type": "object", + "widget": { + "id": "fieldset" + }, + "properties": { + "retainedRoles": { + "title": "label.retained-roles", + "description": "tooltip.retained-roles", + "type": "array", + "items": { + "widget": { + "id": "select" + }, + "type": "string", + "oneOf": [ + { + "enum": [ + "md:SPSSODescriptor" + ], + "description": "value.spdescriptor" + }, + { + "enum": [ + "md:AttributeAuthorityDescriptor" + ], + "description": "value.attr-auth-descriptor" + } + ] + } + }, + "removeRolelessEntityDescriptors": { + "title": "label.remove-roleless-entity-descriptors", + "description": "tooltip.remove-roleless-entity-descriptors", + "type": "boolean", + "default": true + }, + "removeEmptyEntitiesDescriptors": { + "title": "label.remove-empty-entities-descriptors", + "description": "tooltip.remove-empty-entities-descriptors", + "type": "boolean", + "default": true + } + } + } + } + } + } +} \ No newline at end of file diff --git a/ui/src/styles.scss b/ui/src/styles.scss index 454ccba2e..a3f814d8a 100644 --- a/ui/src/styles.scss +++ b/ui/src/styles.scss @@ -6,6 +6,7 @@ @import './theme/alert'; @import './theme/typography'; @import './theme/list'; +@import './theme/utility'; body { background-color: theme-color("light"); diff --git a/ui/src/theme/utility.scss b/ui/src/theme/utility.scss new file mode 100644 index 000000000..f6c3c0e9e --- /dev/null +++ b/ui/src/theme/utility.scss @@ -0,0 +1,7 @@ +.border-2 { + border-width: 2px !important; +} + +.bg-lighter { + background: #FAFAFA !important; +} \ No newline at end of file From 714c1edb9b8283826f78bd6bce812931f737649a Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 6 Jun 2019 07:35:35 -0700 Subject: [PATCH 03/10] SHIBUI-1267 fixed tests --- .../domain/component/editor-nav.component.spec.ts | 3 +-- .../container/dashboard-providers-list.component.spec.ts | 2 ++ .../container/dashboard-providers-list.component.ts | 5 +++-- .../container/dashboard-resolvers-list.component.spec.ts | 8 -------- ui/src/app/wizard/reducer/index.spec.ts | 6 +++--- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/ui/src/app/metadata/domain/component/editor-nav.component.spec.ts b/ui/src/app/metadata/domain/component/editor-nav.component.spec.ts index 3a93f2f8c..3d6627a4e 100644 --- a/ui/src/app/metadata/domain/component/editor-nav.component.spec.ts +++ b/ui/src/app/metadata/domain/component/editor-nav.component.spec.ts @@ -35,8 +35,7 @@ describe('Editor Nav Component', () => { id: 'common', label: 'Common Attributes', index: 2, - initialValues: [], - schema: 'assets/schema/provider/filebacked-http-common.schema.json' + initialValues: [] }; beforeEach(async(() => { diff --git a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.spec.ts b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.spec.ts index 95e8537f0..1a8c447e3 100644 --- a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.spec.ts +++ b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.spec.ts @@ -15,6 +15,7 @@ import { ProviderItemComponent } from '../component/provider-item.component'; import { FileBackedHttpMetadataResolver } from '../../domain/entity'; import { MockI18nModule } from '../../../../testing/i18n.stub'; import { CustomDatePipe } from '../../../shared/pipe/date.pipe'; +import { Observable, of } from 'rxjs'; describe('Dashboard Providers List Page', () => { @@ -61,6 +62,7 @@ describe('Dashboard Providers List Page', () => { modal = TestBed.get(NgbModal); spyOn(store, 'dispatch').and.callThrough(); + spyOn(store, 'select').and.returnValues(of([]), of({'foo': true})); }); it('should compile', () => { diff --git a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts index 35ece7de0..de7c3644f 100644 --- a/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts +++ b/ui/src/app/metadata/manager/container/dashboard-providers-list.component.ts @@ -9,6 +9,7 @@ import { getOpenProviders } from '../reducer'; import { ToggleEntityDisplay } from '../action/manager.action'; import { map } from 'rxjs/operators'; import { ChangeProviderOrderUp, ChangeProviderOrderDown } from '../../provider/action/collection.action'; +import { Metadata } from '../../domain/domain.type'; @Component({ selector: 'dashboard-providers-list', @@ -17,7 +18,7 @@ import { ChangeProviderOrderUp, ChangeProviderOrderDown } from '../../provider/a export class DashboardProvidersListComponent implements OnInit { - providers$: Observable; + providers$: Observable; providersOpen$: Observable<{ [key: string]: boolean }>; constructor( @@ -26,7 +27,7 @@ export class DashboardProvidersListComponent implements OnInit { ) { } ngOnInit(): void { - this.providers$ = this.store.select(getOrderedProviders) as Observable; + this.providers$ = this.store.select(getOrderedProviders); this.providersOpen$ = this.store.select(getOpenProviders); } diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts index d4e81b761..41a8bd30e 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.spec.ts @@ -6,7 +6,6 @@ import { StoreModule, Store, combineReducers } from '@ngrx/store'; import { NgbPaginationModule, NgbModal, NgbModalModule, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import * as fromDashboard from '../reducer'; import { ProviderSearchComponent } from '../component/provider-search.component'; -import { EntityItemComponent } from '../component/entity-item.component'; import { DeleteDialogComponent } from '../component/delete-dialog.component'; import { RouterStub } from '../../../../testing/router.stub'; import { NgbModalStub } from '../../../../testing/modal.stub'; @@ -100,13 +99,6 @@ describe('Dashboard Resolvers List Page', () => { }); }); - describe('openPreviewDialog method', () => { - it('should fire a redux action', () => { - instance.openPreviewDialog(resolver); - expect(store.dispatch).toHaveBeenCalled(); - }); - }); - describe('search method', () => { it('should fire a redux action', () => { instance.search(); diff --git a/ui/src/app/wizard/reducer/index.spec.ts b/ui/src/app/wizard/reducer/index.spec.ts index dd407ba01..5f72dd71f 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.getSchemaPath('common', FileBackedHttpMetadataProviderWizard) - ).toBe(FileBackedHttpMetadataProviderWizard.steps[0].schema); + selectors.getSchemaPath(FileBackedHttpMetadataProviderWizard) + ).toBe(FileBackedHttpMetadataProviderWizard.schema); }); it('should return nothing if no schema is found', () => { expect( - selectors.getSchemaPath('common', null) + selectors.getSchemaPath(null) ).toBeFalsy(); }); }); From 44dea7b8b8d759a720f8a723c06c587385f86c1f Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 6 Jun 2019 09:05:57 -0700 Subject: [PATCH 04/10] SHIBUI-1267 unit tests --- .../metadata-configuration.component.spec.ts | 60 +++++++++++++++++ .../metadata-configuration.component.ts | 6 -- .../container/configuration.component.spec.ts | 64 +++++++++++++++++++ .../container/configuration.component.ts | 3 - .../reducer/configuration.reducer.spec.ts | 51 +++++++++++++++ .../configuration/reducer/index.spec.ts | 19 ++++++ .../domain/model/metadata-resolver.ts | 2 + 7 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts create mode 100644 ui/src/app/metadata/configuration/reducer/index.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 e69de29bb..f26c8d65c 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 @@ -0,0 +1,60 @@ +import { Component, ViewChild, Input } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { MetadataConfigurationComponent } from './metadata-configuration.component'; + +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { MetadataConfiguration } from '../model/metadata-configuration'; +import { Property } from '../../domain/model/property'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; + +@Component({ + selector: 'object-property', + template: `` +}) +class ObjectPropertyComponent { + @Input() property: Property; +} + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(MetadataConfigurationComponent) + public componentUnderTest: MetadataConfigurationComponent; + + configuration: MetadataConfiguration = {sections: []}; +} + +describe('Metadata Configuration Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: MetadataConfigurationComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule, + MockI18nModule, + RouterTestingModule + ], + declarations: [ + MetadataConfigurationComponent, + ObjectPropertyComponent, + TestHostComponent + ], + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should accept a configuration input', async(() => { + expect(app).toBeTruthy(); + })); +}); 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 6b3089f78..732b920d3 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts @@ -1,7 +1,5 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; import { MetadataConfiguration } from '../model/metadata-configuration'; -import { Property } from '../../domain/model/property'; -import Section from '../model/section'; @Component({ selector: 'metadata-configuration', @@ -13,8 +11,4 @@ export class MetadataConfigurationComponent { @Input() configuration: MetadataConfiguration; constructor() { } - - edit(section: Section): void { - console.log(section); - } } diff --git a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts index e69de29bb..a61db7b7c 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts @@ -0,0 +1,64 @@ +import { Component, ViewChild, Input } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, combineReducers } from '@ngrx/store'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; + +import { MetadataConfiguration } from '../model/metadata-configuration'; +import { ConfigurationComponent } from './configuration.component'; +import * as fromConfiguration from '../reducer'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; + +@Component({ + selector: 'metadata-configuration', + template: `` +}) +class MetadataConfigurationComponent { + @Input() configuration: MetadataConfiguration; +} + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ConfigurationComponent) + public componentUnderTest: ConfigurationComponent; + + configuration: MetadataConfiguration = { sections: [] }; +} + +describe('Metadata Configuration Page Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ConfigurationComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule, + StoreModule.forRoot({ + 'metadata-configuration': combineReducers(fromConfiguration.reducers), + }), + MockI18nModule, + RouterTestingModule + ], + declarations: [ + ConfigurationComponent, + MetadataConfigurationComponent, + TestHostComponent + ], + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should load metadata objects', async(() => { + expect(app).toBeTruthy(); + })); +}); diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts index eb52f60ba..e22f23861 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.ts @@ -5,7 +5,6 @@ import { ActivatedRoute } from '@angular/router'; import * as fromConfiguration from '../reducer'; import { MetadataConfiguration } from '../model/metadata-configuration'; -import { METADATA_SOURCE_EDITOR } from '../../resolver/wizard-definition'; import { takeUntil, map } from 'rxjs/operators'; import { LoadMetadataRequest, ClearConfiguration } from '../action/configuration.action'; @@ -30,8 +29,6 @@ export class ConfigurationComponent implements OnDestroy { takeUntil(this.ngUnsubscribe), map(params => new LoadMetadataRequest({id: params.id, type: params.type})) ).subscribe(store); - - this.configuration$.subscribe(c => console.log(c)); } ngOnDestroy() { diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts new file mode 100644 index 000000000..b4d485fd0 --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts @@ -0,0 +1,51 @@ +import { reducer } from './configuration.reducer'; +import * as fromConfig from './configuration.reducer'; +import * as actions from '../action/configuration.action'; +import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { MetadataResolver } from '../../domain/model'; + +describe('Configuration Reducer', () => { + const initialState: fromConfig.State = { ...fromConfig.initialState }; + + describe('undefined action', () => { + it('should return the default state', () => { + const result = reducer(undefined, {} as any); + + expect(result).toEqual(initialState); + }); + }); + + describe('SET_DEFINITION action', () => { + it('should set the state definition', () => { + const definition = new MetadataSourceEditor(); + const action = new actions.SetDefinition(definition); + const result = reducer(initialState, action); + + expect(result).toEqual({ ...initialState, definition }); + }); + }); + + describe('SET_SCHEMA action', () => { + it('should set the state schema', () => { + const action = new actions.SetSchema(SCHEMA); + const result = reducer(initialState, action); + + expect(result).toEqual({ ...initialState, schema: SCHEMA }); + }); + }); + + describe('SET_METADATA action', () => { + it('should set the state metadata model', () => { + const model: MetadataResolver = { + id: 'foo', + serviceProviderName: 'foo', + '@type': 'MetadataResolver' + }; + const action = new actions.SetMetadata(model as MetadataResolver); + const result = reducer(initialState, action); + + expect(result).toEqual({ ...initialState, model }); + }); + }); +}); diff --git a/ui/src/app/metadata/configuration/reducer/index.spec.ts b/ui/src/app/metadata/configuration/reducer/index.spec.ts new file mode 100644 index 000000000..5a675621c --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/index.spec.ts @@ -0,0 +1,19 @@ +import { getConfigurationSectionsFn } from './index'; +import { SCHEMA as schema } from '../../../../testing/form-schema.stub'; +import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; + +describe('Configuration Reducer', () => { + const model = { + name: 'foo', + '@type': 'MetadataResolver' + }; + + const definition = new MetadataSourceEditor(); + + 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/domain/model/metadata-resolver.ts b/ui/src/app/metadata/domain/model/metadata-resolver.ts index 01505cff5..e97c08d07 100644 --- a/ui/src/app/metadata/domain/model/metadata-resolver.ts +++ b/ui/src/app/metadata/domain/model/metadata-resolver.ts @@ -25,4 +25,6 @@ export interface MetadataResolver extends MetadataBase { serviceEnabled?: boolean; relyingPartyOverrides?: RelyingPartyOverrides; attributeRelease?: string[]; + + [property: string]: unknown; } From 43826b184b5b1d308b0fd260cf34d31196aae3e1 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Jun 2019 12:36:25 -0700 Subject: [PATCH 05/10] moved utilities --- .../metadata/configuration/reducer/configuration.reducer.ts | 4 ---- ui/src/app/metadata/configuration/reducer/index.ts | 2 +- ui/src/app/metadata/domain/utility/configuration.spec.ts | 0 .../service/utility.ts => domain/utility/configuration.ts} | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) create mode 100644 ui/src/app/metadata/domain/utility/configuration.spec.ts rename ui/src/app/metadata/{configuration/service/utility.ts => domain/utility/configuration.ts} (96%) diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts index 3acc42338..c526aa22b 100644 --- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts @@ -22,24 +22,20 @@ export function reducer(state = initialState, action: ConfigurationActionsUnion) ...state, schema: action.payload }; - break; case ConfigurationActionTypes.SET_DEFINITION: return { ...state, definition: action.payload }; - break; case ConfigurationActionTypes.SET_METADATA: return { ...state, model: action.payload }; - break; case ConfigurationActionTypes.CLEAR: return { ...initialState }; - break; default: { return state; } diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index 3fb2ae27d..e8ba98467 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -5,7 +5,7 @@ import * as fromRoot from '../../../app.reducer'; import * as fromConfiguration from './configuration.reducer'; import { WizardStep } from '../../../wizard/model'; -import * as utils from '../service/utility'; +import * as utils from '../../domain/utility/configuration'; import { getSplitSchema } from '../../../wizard/reducer'; export interface ConfigurationState { diff --git a/ui/src/app/metadata/domain/utility/configuration.spec.ts b/ui/src/app/metadata/domain/utility/configuration.spec.ts new file mode 100644 index 000000000..e69de29bb diff --git a/ui/src/app/metadata/configuration/service/utility.ts b/ui/src/app/metadata/domain/utility/configuration.ts similarity index 96% rename from ui/src/app/metadata/configuration/service/utility.ts rename to ui/src/app/metadata/domain/utility/configuration.ts index ee329c3b1..0cd0a7a45 100644 --- a/ui/src/app/metadata/configuration/service/utility.ts +++ b/ui/src/app/metadata/domain/utility/configuration.ts @@ -1,4 +1,4 @@ -import { Property } from '../../domain/model/property'; +import { Property } from '../model/property'; export function getDefinition(path: string, definitions: any): any { let def = path.split('/').pop(); From 5b1d54058e8fc5c670a0a87351f1f514f9d30f7d Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Jun 2019 13:55:57 -0700 Subject: [PATCH 06/10] SHIBUI-1267 Updated unit test coverage --- ui/package.json | 2 +- .../effect/configuration.effect.ts | 2 +- .../reducer/configuration.reducer.spec.ts | 33 +++++++--- .../reducer/configuration.reducer.ts | 2 +- .../service/configuration.service.spec.ts | 63 +++++++++++++++++++ .../service/configuration.service.ts | 4 +- .../wizard-summary.component.spec.ts | 13 +--- .../component/wizard-summary.component.ts | 44 +------------ .../domain/utility/configuration.spec.ts | 62 ++++++++++++++++++ .../metadata/domain/utility/configuration.ts | 2 + ui/src/testing/form-schema.stub.ts | 10 ++- 11 files changed, 168 insertions(+), 69 deletions(-) create mode 100644 ui/src/app/metadata/configuration/service/configuration.service.spec.ts diff --git a/ui/package.json b/ui/package.json index 6cfe807cd..60a73244f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -6,7 +6,7 @@ "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", "build": "ng build", - "test": "ng test --code-coverage --source-map=false", + "test": "ng test --code-coverage --source-map=true", "lint": "ng lint", "e2e": "ng e2e", "build:static": "node-sass src/static.scss ./dist/unsecured/static.css", diff --git a/ui/src/app/metadata/configuration/effect/configuration.effect.ts b/ui/src/app/metadata/configuration/effect/configuration.effect.ts index ef86d13fd..781d9a7b9 100644 --- a/ui/src/app/metadata/configuration/effect/configuration.effect.ts +++ b/ui/src/app/metadata/configuration/effect/configuration.effect.ts @@ -42,7 +42,7 @@ export class MetadataConfigurationEffects { @Effect() setDefinition$ = this.actions$.pipe( ofType(ConfigurationActionTypes.SET_METADATA), - map(action => new SetDefinition(this.configService.getDefinition(action.payload))) + map(action => new SetDefinition(this.configService.getDefinition(action.payload['@type']))) ); @Effect() diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts index b4d485fd0..d02b99560 100644 --- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts @@ -2,12 +2,19 @@ import { reducer } from './configuration.reducer'; import * as fromConfig from './configuration.reducer'; import * as actions from '../action/configuration.action'; import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; -import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { SCHEMA as schema } from '../../../../testing/form-schema.stub'; import { MetadataResolver } from '../../domain/model'; describe('Configuration Reducer', () => { const initialState: fromConfig.State = { ...fromConfig.initialState }; + const definition = new MetadataSourceEditor(); + const model: MetadataResolver = { + id: 'foo', + serviceProviderName: 'foo', + '@type': 'MetadataResolver' + }; + describe('undefined action', () => { it('should return the default state', () => { const result = reducer(undefined, {} as any); @@ -18,7 +25,6 @@ describe('Configuration Reducer', () => { describe('SET_DEFINITION action', () => { it('should set the state definition', () => { - const definition = new MetadataSourceEditor(); const action = new actions.SetDefinition(definition); const result = reducer(initialState, action); @@ -28,24 +34,33 @@ describe('Configuration Reducer', () => { describe('SET_SCHEMA action', () => { it('should set the state schema', () => { - const action = new actions.SetSchema(SCHEMA); + const action = new actions.SetSchema(schema); const result = reducer(initialState, action); - expect(result).toEqual({ ...initialState, schema: SCHEMA }); + expect(result).toEqual({ ...initialState, schema }); }); }); describe('SET_METADATA action', () => { it('should set the state metadata model', () => { - const model: MetadataResolver = { - id: 'foo', - serviceProviderName: 'foo', - '@type': 'MetadataResolver' - }; const action = new actions.SetMetadata(model as MetadataResolver); const result = reducer(initialState, action); expect(result).toEqual({ ...initialState, model }); }); }); + + describe('CLEAR action', () => { + it('should clear the state and reset to initial state', () => { + const action = new actions.ClearConfiguration(); + const result = reducer({ + ...initialState, + model, + definition, + schema + }, action); + + expect(result).toEqual(initialState); + }); + }); }); diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts index c526aa22b..966cf634c 100644 --- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts @@ -1,6 +1,6 @@ import { ConfigurationActionTypes, ConfigurationActionsUnion } from '../action/configuration.action'; import { Metadata } from '../../domain/domain.type'; -import { Wizard, WizardStep } from '../../../wizard/model'; +import { Wizard } from '../../../wizard/model'; import { Schema } from '../model/schema'; export interface State { diff --git a/ui/src/app/metadata/configuration/service/configuration.service.spec.ts b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts new file mode 100644 index 000000000..b3588c174 --- /dev/null +++ b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts @@ -0,0 +1,63 @@ +import { TestBed, async, inject } from '@angular/core/testing'; +import { HttpClientModule, HttpRequest } from '@angular/common/http'; +import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; +import { MetadataConfigurationService, PATHS } from './configuration.service'; +import { FileBackedHttpMetadataProviderEditor } from '../../provider/model'; +import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; + +describe(`Attributes Service`, () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientModule, + HttpClientTestingModule + ], + providers: [ + MetadataConfigurationService + ] + }); + }); + + describe('find method', () => { + it(`should send an expected GET request`, async(inject([MetadataConfigurationService, HttpTestingController], + (service: MetadataConfigurationService, backend: HttpTestingController) => { + const type = 'resolver'; + const id = 'foo'; + service.find(id, type).subscribe(); + backend.expectOne((req: HttpRequest) => { + return req.url === `${service.base}/${PATHS[type]}/${id}` + && req.method === 'GET'; + }, `GET metadata by id and type`); + } + ))); + }); + + describe('loadSchema method', () => { + it(`should send an expected GET request`, async(inject([MetadataConfigurationService, HttpTestingController], + (service: MetadataConfigurationService, backend: HttpTestingController) => { + const path = '/foo.json'; + service.loadSchema(path).subscribe(); + backend.expectOne((req: HttpRequest) => { + return req.url === `${path}` + && req.method === 'GET'; + }, `GET schema by path`); + } + ))); + }); + + describe('getDefinition method', () => { + it(`should retrieve the editor definition by model type`, async(inject([MetadataConfigurationService, HttpTestingController], + (service: MetadataConfigurationService, backend: HttpTestingController) => { + const def = service.getDefinition('FileBackedHttpMetadataResolver'); + expect(def).toBe(FileBackedHttpMetadataProviderEditor); + } + ))); + + it(`should instantiate an editor for resolvers`, async(inject([MetadataConfigurationService], + (service: MetadataConfigurationService) => { + const def = service.getDefinition('foo'); + expect(def instanceof MetadataSourceEditor).toBe(true); + } + ))); + }); +}); diff --git a/ui/src/app/metadata/configuration/service/configuration.service.ts b/ui/src/app/metadata/configuration/service/configuration.service.ts index d7f5641d9..2f0420c49 100644 --- a/ui/src/app/metadata/configuration/service/configuration.service.ts +++ b/ui/src/app/metadata/configuration/service/configuration.service.ts @@ -29,8 +29,8 @@ export class MetadataConfigurationService { return this.http.get(`${this.base}/${PATHS[type]}/${id}`); } - getDefinition(model: Metadata): Wizard { - return MetadataProviderEditorTypes.find(def => def.type === model['@type']) || new MetadataSourceEditor(); + getDefinition(type: string): Wizard { + return MetadataProviderEditorTypes.find(def => def.type === type) || new MetadataSourceEditor(); } loadSchema(path: string): Observable { 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 index 335cd634f..107ef7241 100644 --- a/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts +++ b/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts @@ -4,7 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { NgbDropdownModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; -import { getStepProperties, WizardSummaryComponent } from './wizard-summary.component'; +import { WizardSummaryComponent } from './wizard-summary.component'; import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-schema-form'; import { Wizard } from '../../../wizard/model'; import { MetadataProvider } from '../../domain/model'; @@ -68,17 +68,6 @@ describe('Provider Wizard Summary Component', () => { expect(app).toBeTruthy(); })); - describe('getStepProperties function', () => { - it('should return an empty array of schema or schema.properties is not defined', () => { - expect(getStepProperties(null, {})).toEqual([]); - expect(getStepProperties({}, {})).toEqual([]); - }); - - it('should return a formatted list of properties', () => { - expect(getStepProperties(SCHEMA, {}).length).toBe(2); - }); - }); - describe('gotoPage function', () => { it('should emit an empty string if page is null', () => { spyOn(app.onPageSelect, 'emit'); diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.ts b/ui/src/app/metadata/domain/component/wizard-summary.component.ts index fa570a1c0..e8b8deb34 100644 --- a/ui/src/app/metadata/domain/component/wizard-summary.component.ts +++ b/ui/src/app/metadata/domain/component/wizard-summary.component.ts @@ -1,10 +1,11 @@ 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 merge from 'deepmerge'; +import { getStepProperties } from '../utility/configuration'; interface Section { id: string; @@ -14,47 +15,6 @@ interface Section { properties: Property[]; } -export function getDefinition(path: string, definitions: any): any { - let def = path.split('/').pop(); - return definitions[def]; -} - -export function getPropertyItemSchema(items: any, definitions: any): any { - if (!items) { return null; } - return items.$ref ? getDefinition(items.$ref, definitions) : items; -} - -export function getStepProperty(property, model, definitions): Property { - if (!property) { return null; } - property = property.$ref ? { ...property, ...getDefinition(property.$ref, definitions) } : property; - return { - name: property.title, - value: model, - type: property.type, - items: getPropertyItemSchema(property.items, definitions), - properties: getStepProperties( - property, - model, - definitions - ), - widget: property.widget instanceof String ? { id: property.widget } : { ...property.widget } - }; -} - - -export function getStepProperties(schema: any, model: any, definitions: any = {}): Property[] { - if (!schema || !schema.properties) { return []; } - return Object - .keys(schema.properties) - .map(property => { - return getStepProperty( - schema.properties[property], - model && model.hasOwnProperty(property) ? model[property] : null, - definitions - ); - }); -} - @Component({ selector: 'wizard-summary', templateUrl: './wizard-summary.component.html', diff --git a/ui/src/app/metadata/domain/utility/configuration.spec.ts b/ui/src/app/metadata/domain/utility/configuration.spec.ts index e69de29bb..b331a8647 100644 --- a/ui/src/app/metadata/domain/utility/configuration.spec.ts +++ b/ui/src/app/metadata/domain/utility/configuration.spec.ts @@ -0,0 +1,62 @@ +import { getStepProperties, getDefinition, getPropertyItemSchema, getStepProperty } from './configuration'; +import * as utils from './configuration'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; + +describe('domain utility functions', () => { + describe('getStepProperties function', () => { + it('should return an empty array of schema or schema.properties is not defined', () => { + expect(getStepProperties(null, {})).toEqual([]); + expect(getStepProperties({}, {})).toEqual([]); + }); + + it('should return a formatted list of properties', () => { + expect(getStepProperties(SCHEMA, {}).length).toBe(2); + }); + }); + + describe('getDefinitions method', () => { + it('should retrieve the definitions from the json schema', () => { + const definition = { + id: 'foo', + title: 'bar', + description: 'baz', + type: 'string' + }; + expect(getDefinition('/foo/bar', {bar: definition})).toBe(definition); + }); + }); + + describe('getPropertyItemSchema method', () => { + it('should return null if no items are provided', () => { + expect(getPropertyItemSchema(null, SCHEMA.definitions)).toBeNull(); + }); + it('should retrieve the definitions from the items schema', () => { + expect(getPropertyItemSchema({$ref: 'description'}, SCHEMA.definitions)).toBe(SCHEMA.definitions.description); + }); + it('should return the item itself if no $ref', () => { + let item = {}; + expect(getPropertyItemSchema(item, SCHEMA.definitions)).toBe(item); + }); + }); + + describe('getStepProperty method', () => { + const model = { + name: 'foo', + type: 'bar', + description: 'baz' + }; + it('should return null if no items are provided', () => { + expect(getStepProperty(null, null, SCHEMA.definitions)).toBeNull(); + }); + + it('should retrieve the property $ref definition if available', () => { + const property = getStepProperty( + { $ref: 'description' }, + model, + SCHEMA.definitions + ); + expect(property.type).toBe('string'); + }); + }); +}); + diff --git a/ui/src/app/metadata/domain/utility/configuration.ts b/ui/src/app/metadata/domain/utility/configuration.ts index 0cd0a7a45..dc641c4dd 100644 --- a/ui/src/app/metadata/domain/utility/configuration.ts +++ b/ui/src/app/metadata/domain/utility/configuration.ts @@ -4,6 +4,7 @@ export function getDefinition(path: string, definitions: any): any { let def = path.split('/').pop(); return definitions[def]; } + export function getPropertyItemSchema(items: any, definitions: any): any { if (!items) { return null; } return items.$ref ? getDefinition(items.$ref, definitions) : items; @@ -26,6 +27,7 @@ export function getStepProperty(property, model, definitions): Property { }; } + export function getStepProperties(schema: any, model: any, definitions: any = {}): Property[] { if (!schema || !schema.properties) { return []; } return Object diff --git a/ui/src/testing/form-schema.stub.ts b/ui/src/testing/form-schema.stub.ts index c91f63bf1..f317b1199 100644 --- a/ui/src/testing/form-schema.stub.ts +++ b/ui/src/testing/form-schema.stub.ts @@ -44,5 +44,13 @@ export const SCHEMA = { '@type' ] } - ] + ], + 'definitions': { + 'description': { + 'title': 'Description', + 'description': 'A description of the object', + 'type': 'string', + 'widget': 'string' + } + } }; From e0298a4df051ac4e06c58b522151d6b5f1623abd Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 7 Jun 2019 14:41:55 -0700 Subject: [PATCH 07/10] SHIBUI-1267 Updated unit test coverage --- .../array-property.component.spec.ts | 115 ++++++++++++++++++ .../component/array-property.component.ts | 4 - .../configuration-property.component.spec.ts | 69 +++++++++++ .../configuration-property.component.ts | 6 +- .../object-property.component.spec.ts | 60 +++++++++ .../component/object-property.component.ts | 8 -- .../primitive-property.component.spec.ts | 60 +++++++++ .../domain/utility/configuration.spec.ts | 2 +- ui/src/testing/form-schema.stub.ts | 25 ++++ 9 files changed, 335 insertions(+), 14 deletions(-) create mode 100644 ui/src/app/metadata/configuration/component/array-property.component.spec.ts create mode 100644 ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts create mode 100644 ui/src/app/metadata/configuration/component/object-property.component.spec.ts create mode 100644 ui/src/app/metadata/configuration/component/primitive-property.component.spec.ts diff --git a/ui/src/app/metadata/configuration/component/array-property.component.spec.ts b/ui/src/app/metadata/configuration/component/array-property.component.spec.ts new file mode 100644 index 000000000..453d9d484 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/array-property.component.spec.ts @@ -0,0 +1,115 @@ +import { Component, ViewChild, Input } 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 { Property } from '../../domain/model/property'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { getStepProperty } from '../../domain/utility/configuration'; +import { ArrayPropertyComponent } from './array-property.component'; +import { AttributesService } from '../../domain/service/attributes.service'; +import { MockAttributeService } from '../../../../testing/attributes.stub'; +import { of } from 'rxjs'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ArrayPropertyComponent) + public componentUnderTest: ArrayPropertyComponent; + + property: Property = getStepProperty(SCHEMA.properties.list, { + name: 'foo', + type: 'baz', + description: 'foo bar baz', + list: [] + }, SCHEMA.definitions); + + setProperty(property: Property): void { + this.property = property; + } +} + +describe('Array Property Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ArrayPropertyComponent; + let service: AttributesService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbPopoverModule, + MockI18nModule, + RouterTestingModule + ], + declarations: [ + ArrayPropertyComponent, + TestHostComponent + ], + providers: [ + { provide: AttributesService, useClass: MockAttributeService } + ] + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + service = TestBed.get(AttributesService); + fixture.detectChanges(); + })); + + it('should accept a property input', async(() => { + expect(app).toBeTruthy(); + })); + + describe('attributeList$ getter', () => { + it('should return an empty list when no data or dataUrl is set', () => { + app.attributeList$.subscribe((list) => { + expect(list).toEqual([]); + }); + }); + it('should return a list of data items from the schema', () => { + const datalist = [ + { key: 'foo', label: 'foo' }, + { key: 'bar', label: 'bar' }, + { key: 'baz', label: 'baz' }, + ]; + instance.setProperty({ + ...instance.property, + widget: { + id: 'datalist', + data: datalist + } + }); + fixture.detectChanges(); + app.attributeList$.subscribe(list => { + expect(list).toEqual(datalist); + }); + }); + + it('should call the attribute service with a provided dataUrl', () => { + const datalist = [ + { key: 'foo', label: 'foo' }, + { key: 'bar', label: 'bar' }, + { key: 'baz', label: 'baz' }, + ]; + spyOn(service, 'query').and.returnValue(of(datalist)); + instance.setProperty({ + ...instance.property, + widget: { + id: 'datalist', + dataUrl: '/foo' + } + }); + fixture.detectChanges(); + app.attributeList$.subscribe(list => { + expect(list).toEqual(datalist); + }); + }); + }); +}); diff --git a/ui/src/app/metadata/configuration/component/array-property.component.ts b/ui/src/app/metadata/configuration/component/array-property.component.ts index 77c8fcab0..ff9cd0ac4 100644 --- a/ui/src/app/metadata/configuration/component/array-property.component.ts +++ b/ui/src/app/metadata/configuration/component/array-property.component.ts @@ -19,10 +19,6 @@ export class ArrayPropertyComponent extends ConfigurationPropertyComponent { super(); } - getKeys(schema): string[] { - return Object.keys(schema.properties); - } - get attributeList$(): Observable<{ key: string, label: string }[]> { if (this.property.widget && this.property.widget.hasOwnProperty('data')) { return of(this.property.widget.data); diff --git a/ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts b/ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts new file mode 100644 index 000000000..5ef0452e0 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts @@ -0,0 +1,69 @@ +import { Component, ViewChild, Input } 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 { Property } from '../../domain/model/property'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { getStepProperties, getStepProperty } from '../../domain/utility/configuration'; +import { ConfigurationPropertyComponent } from './configuration-property.component'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ConfigurationPropertyComponent) + public componentUnderTest: ConfigurationPropertyComponent; + + property: Property = getStepProperty(SCHEMA.properties.name, { + name: 'foo', + type: 'baz', + description: 'foo bar baz' + }, SCHEMA.definitions); +} + +describe('Configuration Property Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ConfigurationPropertyComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbPopoverModule, + MockI18nModule, + RouterTestingModule + ], + declarations: [ + ConfigurationPropertyComponent, + TestHostComponent + ] + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should accept a property input', async(() => { + expect(app).toBeTruthy(); + })); + + describe('getKeys method', () => { + it('should return the property`s child keys', () => { + expect(app.getKeys({ properties: { foo: 'bar', baz: 'bar' } })).toEqual(['foo', 'baz']); + }); + }); + + describe('getItemType method', () => { + it('should return the item`s type', () => { + expect(app.getItemType({ widget: { id: 'string' } } as Property)).toBe('string'); + expect(app.getItemType({} as Property)).toBe('default'); + }); + }); +}); diff --git a/ui/src/app/metadata/configuration/component/configuration-property.component.ts b/ui/src/app/metadata/configuration/component/configuration-property.component.ts index 91e97e87b..bcdd45711 100644 --- a/ui/src/app/metadata/configuration/component/configuration-property.component.ts +++ b/ui/src/app/metadata/configuration/component/configuration-property.component.ts @@ -3,7 +3,7 @@ import { Property } from '../../domain/model/property'; @Component({ selector: 'configuration-property', - templateUrl: './configuration-property.component.html', + template: `{{ property | json }}`, styleUrls: [] }) @@ -12,6 +12,10 @@ export class ConfigurationPropertyComponent { constructor() { } + getKeys(schema): string[] { + return Object.keys(schema.properties); + } + getItemType(items: Property): string { return items.widget ? items.widget.id : 'default'; } diff --git a/ui/src/app/metadata/configuration/component/object-property.component.spec.ts b/ui/src/app/metadata/configuration/component/object-property.component.spec.ts new file mode 100644 index 000000000..6299b20d5 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/object-property.component.spec.ts @@ -0,0 +1,60 @@ +import { Component, ViewChild, Input } 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 { Property } from '../../domain/model/property'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { ObjectPropertyComponent } from './object-property.component'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { getStepProperties, getStepProperty } from '../../domain/utility/configuration'; +import { PrimitivePropertyComponent } from './primitive-property.component'; +import { ArrayPropertyComponent } from './array-property.component'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(ObjectPropertyComponent) + public componentUnderTest: ObjectPropertyComponent; + + property: Property = getStepProperty(SCHEMA.properties.name, { + name: 'foo', + type: 'baz', + description: 'foo bar baz' + }, SCHEMA.definitions); +} + +describe('Object Property Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: ObjectPropertyComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbPopoverModule, + MockI18nModule, + RouterTestingModule + ], + declarations: [ + ObjectPropertyComponent, + PrimitivePropertyComponent, + ArrayPropertyComponent, + TestHostComponent + ] + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should accept a property input', async(() => { + expect(app).toBeTruthy(); + })); +}); diff --git a/ui/src/app/metadata/configuration/component/object-property.component.ts b/ui/src/app/metadata/configuration/component/object-property.component.ts index db107da92..f7892877e 100644 --- a/ui/src/app/metadata/configuration/component/object-property.component.ts +++ b/ui/src/app/metadata/configuration/component/object-property.component.ts @@ -14,13 +14,5 @@ export class ObjectPropertyComponent extends ConfigurationPropertyComponent { constructor() { super(); } - - getKeys(schema): string[] { - return Object.keys(schema.properties); - } - - getItemType(items: Property): string { - return items.widget ? items.widget.id : 'default'; - } } diff --git a/ui/src/app/metadata/configuration/component/primitive-property.component.spec.ts b/ui/src/app/metadata/configuration/component/primitive-property.component.spec.ts new file mode 100644 index 000000000..9ce81bfb5 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/primitive-property.component.spec.ts @@ -0,0 +1,60 @@ +import { Component, ViewChild, Input } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; +import { Property } from '../../domain/model/property'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { PrimitivePropertyComponent } from './primitive-property.component'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(PrimitivePropertyComponent) + public componentUnderTest: PrimitivePropertyComponent; + + property: Property = { + title: 'foo', + type: 'string', + name: 'foo', + value: ['bar'], + items: null, + properties: null, + widget: { + id: 'string' + } + }; +} + +describe('Primitive Property Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: PrimitivePropertyComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule, + MockI18nModule, + RouterTestingModule + ], + declarations: [ + PrimitivePropertyComponent, + TestHostComponent + ], + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should accept a property input', async(() => { + expect(app).toBeTruthy(); + })); +}); diff --git a/ui/src/app/metadata/domain/utility/configuration.spec.ts b/ui/src/app/metadata/domain/utility/configuration.spec.ts index b331a8647..d9a5a653f 100644 --- a/ui/src/app/metadata/domain/utility/configuration.spec.ts +++ b/ui/src/app/metadata/domain/utility/configuration.spec.ts @@ -10,7 +10,7 @@ describe('domain utility functions', () => { }); it('should return a formatted list of properties', () => { - expect(getStepProperties(SCHEMA, {}).length).toBe(2); + expect(getStepProperties(SCHEMA, {}).length).toBe(3); }); }); diff --git a/ui/src/testing/form-schema.stub.ts b/ui/src/testing/form-schema.stub.ts index f317b1199..1ef7d42ae 100644 --- a/ui/src/testing/form-schema.stub.ts +++ b/ui/src/testing/form-schema.stub.ts @@ -30,6 +30,31 @@ export const SCHEMA = { 'description': 'FileBackedHttpMetadataProvider' } ] + }, + 'list': { + 'title': 'label.retained-roles', + 'description': 'tooltip.retained-roles', + 'type': 'array', + 'items': { + 'widget': { + 'id': 'select' + }, + 'type': 'string', + 'oneOf': [ + { + 'enum': [ + 'SPSSODescriptor' + ], + 'description': 'value.spdescriptor' + }, + { + 'enum': [ + 'AttributeAuthorityDescriptor' + ], + 'description': 'value.attr-auth-descriptor' + } + ] + } } }, 'required': [ From 2c27f1c2b503f1550ef7f8d3e652c2e20698010e Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Mon, 10 Jun 2019 08:48:06 -0700 Subject: [PATCH 08/10] SHIBUI-1267 Updated translation strings --- backend/src/main/resources/i18n/messages.properties | 1 + .../component/metadata-configuration.component.html | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 7cadee855..03ae27513 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -236,6 +236,7 @@ label.filter-name=Filter Name label.filter-enabled=Filter Enabled label.filter-target=FilterTarget label.filter-type=Filter Type +label.option=Option label.value=Value label.binding-type=Binding Type label.sign-assertion=Sign Assertions 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 30e133525..136e625d7 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html @@ -9,13 +9,13 @@

- Option - Value + Option + Value
From 0b387ccb2bef42d69eda2b2b339ff027b4c196f9 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 11 Jun 2019 07:36:48 -0700 Subject: [PATCH 09/10] SHIBUI-1269 Implemented configuration xml component --- .../action/configuration.action.ts | 41 +++++++++++ .../metadata-configuration.component.html | 2 +- .../configuration/configuration.module.ts | 4 ++ .../configuration/configuration.routing.ts | 18 ++++- .../container/configuration.component.html | 2 +- .../container/configuration.component.spec.ts | 9 --- .../container/configuration.component.ts | 6 +- .../container/metadata-options.component.html | 3 + .../metadata-options.component.spec.ts | 70 +++++++++++++++++++ .../container/metadata-options.component.ts | 23 ++++++ .../container/metadata-xml.component.html | 7 ++ .../container/metadata-xml.component.spec.ts | 61 ++++++++++++++++ .../container/metadata-xml.component.ts | 33 +++++++++ .../effect/configuration.effect.ts | 63 +++++++++++++++-- .../reducer/configuration.reducer.spec.ts | 39 +++++++++++ .../reducer/configuration.reducer.ts | 10 ++- .../metadata/configuration/reducer/index.ts | 1 + .../dashboard-resolvers-list.component.ts | 3 +- 18 files changed, 372 insertions(+), 23 deletions(-) create mode 100644 ui/src/app/metadata/configuration/container/metadata-options.component.html create mode 100644 ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts create mode 100644 ui/src/app/metadata/configuration/container/metadata-options.component.ts create mode 100644 ui/src/app/metadata/configuration/container/metadata-xml.component.html create mode 100644 ui/src/app/metadata/configuration/container/metadata-xml.component.spec.ts create mode 100644 ui/src/app/metadata/configuration/container/metadata-xml.component.ts diff --git a/ui/src/app/metadata/configuration/action/configuration.action.ts b/ui/src/app/metadata/configuration/action/configuration.action.ts index 6c71b61a6..1aa442601 100644 --- a/ui/src/app/metadata/configuration/action/configuration.action.ts +++ b/ui/src/app/metadata/configuration/action/configuration.action.ts @@ -12,9 +12,17 @@ export enum ConfigurationActionTypes { LOAD_SCHEMA_SUCCESS = '[Metadata Configuration] Load Schema Success', LOAD_SCHEMA_ERROR = '[Metadata Configuration] Load Schema Error', + LOAD_XML_REQUEST = '[Metadata Configuration] Load XML Request', + LOAD_XML_SUCCESS = '[Metadata Configuration] Load XML Success', + LOAD_XML_ERROR = '[Metadata Configuration] Load XML Error', + SET_METADATA = '[Metadata Configuration] Set Metadata Model', SET_DEFINITION = '[Metadata Configuration] Set Metadata Definition', SET_SCHEMA = '[Metadata Configuration] Set Metadata Schema', + SET_XML = '[Metadata Configuration] Set Metadata Xml', + + DOWNLOAD_XML = '[Metadata Configuration] Download Metadata Xml', + CLEAR = '[Metadata Configuration] Clear' } @@ -54,6 +62,24 @@ export class LoadSchemaError implements Action { constructor(public payload: any) { } } +export class LoadXmlRequest implements Action { + readonly type = ConfigurationActionTypes.LOAD_XML_REQUEST; + + constructor(public payload: string) { } +} + +export class LoadXmlSuccess implements Action { + readonly type = ConfigurationActionTypes.LOAD_XML_SUCCESS; + + constructor(public payload: string) { } +} + +export class LoadXmlError implements Action { + readonly type = ConfigurationActionTypes.LOAD_XML_ERROR; + + constructor(public payload: any) { } +} + export class SetMetadata implements Action { readonly type = ConfigurationActionTypes.SET_METADATA; @@ -72,6 +98,16 @@ export class SetSchema implements Action { constructor(public payload: Schema) { } } +export class SetXml implements Action { + readonly type = ConfigurationActionTypes.SET_XML; + + constructor(public payload: string) { } +} + +export class DownloadXml implements Action { + readonly type = ConfigurationActionTypes.DOWNLOAD_XML; +} + export class ClearConfiguration implements Action { readonly type = ConfigurationActionTypes.CLEAR; } @@ -83,7 +119,12 @@ export type ConfigurationActionsUnion = | LoadSchemaRequest | LoadSchemaSuccess | LoadSchemaError + | LoadXmlRequest + | LoadXmlSuccess + | LoadXmlError | SetMetadata | SetDefinition | SetSchema + | SetXml + | DownloadXml | ClearConfiguration; 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 136e625d7..258b9aa77 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/ui/src/app/metadata/configuration/configuration.module.ts b/ui/src/app/metadata/configuration/configuration.module.ts index eb69b14af..2cf56580d 100644 --- a/ui/src/app/metadata/configuration/configuration.module.ts +++ b/ui/src/app/metadata/configuration/configuration.module.ts @@ -17,10 +17,14 @@ import { PrimitivePropertyComponent } from './component/primitive-property.compo import { ObjectPropertyComponent } from './component/object-property.component'; import { ArrayPropertyComponent } from './component/array-property.component'; import { RouterModule } from '@angular/router'; +import { MetadataOptionsComponent } from './container/metadata-options.component'; +import { MetadataXmlComponent } from './container/metadata-xml.component'; @NgModule({ declarations: [ MetadataConfigurationComponent, + MetadataOptionsComponent, + MetadataXmlComponent, ConfigurationPropertyComponent, PrimitivePropertyComponent, ObjectPropertyComponent, diff --git a/ui/src/app/metadata/configuration/configuration.routing.ts b/ui/src/app/metadata/configuration/configuration.routing.ts index c1e6e138d..8e7327792 100644 --- a/ui/src/app/metadata/configuration/configuration.routing.ts +++ b/ui/src/app/metadata/configuration/configuration.routing.ts @@ -1,9 +1,25 @@ import { Routes } from '@angular/router'; import { ConfigurationComponent } from './container/configuration.component'; +import { MetadataOptionsComponent } from './container/metadata-options.component'; +import { MetadataXmlComponent } from './container/metadata-xml.component'; export const ConfigurationRoutes: Routes = [ { path: ':type/:id/configuration', - component: ConfigurationComponent + component: ConfigurationComponent, + children: [ + { + path: '', + redirectTo: 'options' + }, + { + path: 'options', + component: MetadataOptionsComponent + }, + { + path: 'xml', + component: MetadataXmlComponent + } + ] } ]; diff --git a/ui/src/app/metadata/configuration/container/configuration.component.html b/ui/src/app/metadata/configuration/container/configuration.component.html index 1e9f67486..36626f240 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.html +++ b/ui/src/app/metadata/configuration/container/configuration.component.html @@ -11,7 +11,7 @@
- +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts index a61db7b7c..c399fbacf 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts @@ -9,14 +9,6 @@ import { ConfigurationComponent } from './configuration.component'; import * as fromConfiguration from '../reducer'; import { MockI18nModule } from '../../../../testing/i18n.stub'; -@Component({ - selector: 'metadata-configuration', - template: `` -}) -class MetadataConfigurationComponent { - @Input() configuration: MetadataConfiguration; -} - @Component({ template: ` @@ -47,7 +39,6 @@ describe('Metadata Configuration Page Component', () => { ], declarations: [ ConfigurationComponent, - MetadataConfigurationComponent, TestHostComponent ], }).compileComponents(); diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts index e22f23861..d18e5ab34 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.ts @@ -1,7 +1,7 @@ import { Store } from '@ngrx/store'; import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; import { Observable, Subject } from 'rxjs'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Params } from '@angular/router'; import * as fromConfiguration from '../reducer'; import { MetadataConfiguration } from '../model/metadata-configuration'; @@ -17,14 +17,10 @@ import { LoadMetadataRequest, ClearConfiguration } from '../action/configuration export class ConfigurationComponent implements OnDestroy { private ngUnsubscribe: Subject = new Subject(); - configuration$: Observable; - constructor( private store: Store, private routerState: ActivatedRoute ) { - this.configuration$ = this.store.select(fromConfiguration.getConfigurationSections); - this.routerState.params.pipe( takeUntil(this.ngUnsubscribe), map(params => new LoadMetadataRequest({id: params.id, type: params.type})) diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.html b/ui/src/app/metadata/configuration/container/metadata-options.component.html new file mode 100644 index 000000000..5e6b85384 --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts b/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts new file mode 100644 index 000000000..2e35d8c75 --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts @@ -0,0 +1,70 @@ +import { Component, ViewChild, Input } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, combineReducers, Store } from '@ngrx/store'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; + +import { MetadataConfiguration } from '../model/metadata-configuration'; +import * as fromConfiguration from '../reducer'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { MetadataOptionsComponent } from './metadata-options.component'; + +@Component({ + selector: 'metadata-configuration', + template: `` +}) +class MetadataConfigurationComponent { + @Input() configuration: MetadataConfiguration; +} + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(MetadataOptionsComponent) + public componentUnderTest: MetadataOptionsComponent; + + configuration: MetadataConfiguration = { sections: [] }; +} + +describe('Metadata Options Page Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: MetadataOptionsComponent; + let store: Store; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule, + StoreModule.forRoot({ + 'metadata-configuration': combineReducers(fromConfiguration.reducers), + }), + MockI18nModule, + RouterTestingModule + ], + declarations: [ + MetadataOptionsComponent, + MetadataConfigurationComponent, + TestHostComponent + ], + }).compileComponents(); + + store = TestBed.get(Store); + spyOn(store, 'dispatch'); + spyOn(store, 'select'); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should load metadata objects', async(() => { + expect(app).toBeTruthy(); + expect(store.select).toHaveBeenCalled(); + })); +}); diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.ts b/ui/src/app/metadata/configuration/container/metadata-options.component.ts new file mode 100644 index 000000000..bc2d0bb0d --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.ts @@ -0,0 +1,23 @@ +import { Store } from '@ngrx/store'; +import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { Observable } from 'rxjs'; + +import * as fromConfiguration from '../reducer'; +import { MetadataConfiguration } from '../model/metadata-configuration'; + +@Component({ + selector: 'metadata-options-page', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './metadata-options.component.html', + styleUrls: [] +}) +export class MetadataOptionsComponent { + + configuration$: Observable; + + constructor( + private store: Store + ) { + this.configuration$ = this.store.select(fromConfiguration.getConfigurationSections); + } +} diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.html b/ui/src/app/metadata/configuration/container/metadata-xml.component.html new file mode 100644 index 000000000..c76b4627b --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.html @@ -0,0 +1,7 @@ +
+
{{ xml$ | async }}
+ +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.spec.ts b/ui/src/app/metadata/configuration/container/metadata-xml.component.spec.ts new file mode 100644 index 000000000..81fbd836f --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.spec.ts @@ -0,0 +1,61 @@ +import { Component, ViewChild, Input } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { StoreModule, combineReducers, Store } from '@ngrx/store'; +import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; + +import { MetadataConfiguration } from '../model/metadata-configuration'; +import * as fromConfiguration from '../reducer'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { MetadataXmlComponent } from './metadata-xml.component'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(MetadataXmlComponent) + public componentUnderTest: MetadataXmlComponent; + + configuration: MetadataConfiguration = { sections: [] }; +} + +describe('Metadata Xml Page Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: MetadataXmlComponent; + let store: Store; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbDropdownModule, + StoreModule.forRoot({ + 'metadata-configuration': combineReducers(fromConfiguration.reducers), + }), + MockI18nModule, + RouterTestingModule + ], + declarations: [ + MetadataXmlComponent, + TestHostComponent + ], + }).compileComponents(); + + store = TestBed.get(Store); + spyOn(store, 'dispatch'); + spyOn(store, 'select'); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should load metadata objects', async(() => { + expect(app).toBeTruthy(); + expect(store.select).toHaveBeenCalledTimes(2); + })); +}); diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.ts b/ui/src/app/metadata/configuration/container/metadata-xml.component.ts new file mode 100644 index 000000000..26b62323e --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.ts @@ -0,0 +1,33 @@ +import { Store } from '@ngrx/store'; +import { Component } from '@angular/core'; +import { Observable, Subject } from 'rxjs'; + +import * as fromConfiguration from '../reducer'; +import { Metadata } from '../../domain/domain.type'; +import { DownloadXml } from '../action/configuration.action'; + +@Component({ + selector: 'metadata-xml-page', + templateUrl: './metadata-xml.component.html', + styleUrls: [] +}) +export class MetadataXmlComponent { + + private ngUnsubscribe: Subject = new Subject(); + + entity: Metadata; + entity$: Observable; + xml: string; + xml$: Observable; + + constructor( + private store: Store + ) { + this.xml$ = this.store.select(fromConfiguration.getConfigurationXml); + this.entity$ = this.store.select(fromConfiguration.getConfigurationModel); + } + + preview(): void { + this.store.dispatch(new DownloadXml()); + } +} diff --git a/ui/src/app/metadata/configuration/effect/configuration.effect.ts b/ui/src/app/metadata/configuration/effect/configuration.effect.ts index 781d9a7b9..ff76c8125 100644 --- a/ui/src/app/metadata/configuration/effect/configuration.effect.ts +++ b/ui/src/app/metadata/configuration/effect/configuration.effect.ts @@ -1,7 +1,9 @@ import { Injectable } from '@angular/core'; import { Effect, Actions, ofType } from '@ngrx/effects'; -import { switchMap, catchError, map } from 'rxjs/operators'; -import { of } from 'rxjs'; +import { switchMap, catchError, map, tap, withLatestFrom } from 'rxjs/operators'; +import { of, Observable } from 'rxjs'; +import * as FileSaver from 'file-saver'; +import { Store } from '@ngrx/store'; import { MetadataConfigurationService } from '../service/configuration.service'; import { @@ -14,8 +16,17 @@ import { LoadSchemaRequest, LoadSchemaSuccess, SetSchema, - LoadSchemaError + LoadSchemaError, + LoadXmlSuccess, + LoadXmlError, + SetXml, + DownloadXml } from '../action/configuration.action'; +import { ResolverService } from '../../domain/service/resolver.service'; +import { EntityIdService } from '../../domain/service/entity-id.service'; +import { State } from '../reducer/configuration.reducer'; +import { getConfigurationModel, getConfigurationXml } from '../reducer'; +import { MetadataResolver } from '../../domain/model'; @Injectable() export class MetadataConfigurationEffects { @@ -33,6 +44,33 @@ export class MetadataConfigurationEffects { ) ); + @Effect() + loadMetadataXml$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.LOAD_METADATA_REQUEST), + switchMap(action => { + let loader: Observable; + switch (action.payload.type) { + case 'filter': + loader = this.entityService.preview(action.payload.id); + break; + default: + loader = this.providerService.preview(action.payload.id); + break; + } + + return loader.pipe( + map(xml => new LoadXmlSuccess(xml)), + catchError(error => of(new LoadXmlError(error))) + ); + }) + ); + + @Effect() + setXmlOnLoad$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.LOAD_XML_SUCCESS), + map(action => new SetXml(action.payload)) + ); + @Effect() setMetadataOnLoad$ = this.actions$.pipe( ofType(ConfigurationActionTypes.LOAD_METADATA_SUCCESS), @@ -70,8 +108,25 @@ export class MetadataConfigurationEffects { map(action => new SetSchema(action.payload)) ); + @Effect({dispatch: false}) + downloadXml$ = this.actions$.pipe( + ofType(ConfigurationActionTypes.DOWNLOAD_XML), + withLatestFrom( + this.store.select(getConfigurationModel), + this.store.select(getConfigurationXml) + ), + tap(([action, entity, xml]) => { + const name = entity.name ? entity.name : (entity as MetadataResolver).serviceProviderName; + const blob = new Blob([xml], { type: 'text/xml;charset=utf-8' }); + FileSaver.saveAs(blob, `${name}.xml`); + }) + ); + constructor( private configService: MetadataConfigurationService, - private actions$: Actions + private actions$: Actions, + private providerService: ResolverService, + private entityService: EntityIdService, + private store: Store ) { } } diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts index d02b99560..fed39ff3f 100644 --- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts @@ -14,6 +14,7 @@ describe('Configuration Reducer', () => { serviceProviderName: 'foo', '@type': 'MetadataResolver' }; + const xml = ``; describe('undefined action', () => { it('should return the default state', () => { @@ -50,6 +51,15 @@ describe('Configuration Reducer', () => { }); }); + describe('SET_XML action', () => { + it('should set the state metadata model', () => { + const action = new actions.SetXml(xml); + const result = reducer(initialState, action); + + expect(result).toEqual({ ...initialState, xml }); + }); + }); + describe('CLEAR action', () => { it('should clear the state and reset to initial state', () => { const action = new actions.ClearConfiguration(); @@ -63,4 +73,33 @@ describe('Configuration Reducer', () => { expect(result).toEqual(initialState); }); }); + + describe('selector functions', () => { + /* + export const getModel = (state: State) => state.model; + export const getDefinition = (state: State) => state.definition; + export const getSchema = (state: State) => state.schema; + export const getXml = (state: State) => state.xml; + */ + describe('getModel', () => { + it('should retrieve the model from state', () => { + expect(fromConfig.getModel({...initialState, model})).toBe(model); + }); + }); + describe('getDefinition', () => { + it('should retrieve the definition from state', () => { + expect(fromConfig.getDefinition({ ...initialState, definition })).toBe(definition); + }); + }); + describe('getSchema', () => { + it('should retrieve the schema from state', () => { + expect(fromConfig.getSchema({ ...initialState, schema })).toBe(schema); + }); + }); + describe('getXml', () => { + it('should retrieve the schema from state', () => { + expect(fromConfig.getXml({ ...initialState, xml })).toBe(xml); + }); + }); + }); }); diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts index 966cf634c..a35e20dd7 100644 --- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts @@ -7,12 +7,14 @@ export interface State { model: Metadata; schema: Schema; definition: Wizard; + xml: string; } export const initialState: State = { model: null, schema: null, - definition: null + definition: null, + xml: '' }; export function reducer(state = initialState, action: ConfigurationActionsUnion): State { @@ -32,6 +34,11 @@ export function reducer(state = initialState, action: ConfigurationActionsUnion) ...state, model: action.payload }; + case ConfigurationActionTypes.SET_XML: + return { + ...state, + xml: action.payload + }; case ConfigurationActionTypes.CLEAR: return { ...initialState @@ -45,3 +52,4 @@ export function reducer(state = initialState, action: ConfigurationActionsUnion) export const getModel = (state: State) => state.model; export const getDefinition = (state: State) => state.definition; export const getSchema = (state: State) => state.schema; +export const getXml = (state: State) => state.xml; diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index e8ba98467..d4c2fcc9c 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -28,6 +28,7 @@ export const getConfigurationState = createSelector(getState, getConfigurationSt export const getConfigurationModel = createSelector(getConfigurationState, fromConfiguration.getModel); export const getConfigurationDefinition = createSelector(getConfigurationState, fromConfiguration.getDefinition); export const getConfigurationSchema = createSelector(getConfigurationState, fromConfiguration.getSchema); +export const getConfigurationXml = createSelector(getConfigurationState, fromConfiguration.getXml); export const getConfigurationSectionsFn = (model, definition, schema) => !definition || !schema ? null : ({ diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts index 0c20310c7..4de58be3a 100644 --- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts +++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts @@ -87,7 +87,8 @@ export class DashboardResolversListComponent implements OnInit { } viewConfiguration(entity: MetadataEntity): void { - this.router.navigate(['metadata', 'resolver', entity.getId(), 'configuration']); + // this.store.dispatch(new PreviewEntity({ id: entity.getId(), entity })); + this.router.navigate(['metadata', 'resolver', entity.getId(), 'configuration', 'options']); } viewMetadataHistory(entity: MetadataEntity): void { From de3d93b7ed614b1dfa687e6f6fb93d82d188eab9 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 11 Jun 2019 07:42:36 -0700 Subject: [PATCH 10/10] SHIBUI-1267 fixed compile error --- .../configuration/container/metadata-xml.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.html b/ui/src/app/metadata/configuration/container/metadata-xml.component.html index c76b4627b..57560ea78 100644 --- a/ui/src/app/metadata/configuration/container/metadata-xml.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.html @@ -1,6 +1,6 @@
{{ xml$ | async }}
-