-
+
+
+
diff --git a/ui/src/app/metadata/provider/container/provider-wizard.component.ts b/ui/src/app/metadata/provider/container/provider-wizard.component.ts
index ee18b72b0..4a599f33e 100644
--- a/ui/src/app/metadata/provider/container/provider-wizard.component.ts
+++ b/ui/src/app/metadata/provider/container/provider-wizard.component.ts
@@ -4,7 +4,7 @@ import { Store } from '@ngrx/store';
import * as fromProvider from '../reducer';
import * as fromWizard from '../../../wizard/reducer';
-import { SetIndex, SetDisabled, ClearWizard } from '../../../wizard/action/wizard.action';
+import { SetIndex, SetDisabled, ClearWizard, SetDefinition } from '../../../wizard/action/wizard.action';
import { LoadSchemaRequest, ClearEditor } from '../action/editor.action';
import { startWith } from 'rxjs/operators';
import { Wizard, WizardStep } from '../../../wizard/model';
@@ -13,7 +13,7 @@ import { ClearProvider } from '../action/entity.action';
import { Router, ActivatedRoute } from '@angular/router';
import { map } from 'rxjs/operators';
import { AddProviderRequest } from '../action/collection.action';
-
+import { MetadataProviderWizard } from '../model';
@Component({
selector: 'provider-wizard',
@@ -67,6 +67,9 @@ export class ProviderWizardComponent implements OnDestroy {
);
this.changes$.subscribe(c => this.provider = c);
+
+ this.store.dispatch(new SetDefinition(MetadataProviderWizard));
+ this.store.dispatch(new SetIndex(MetadataProviderWizard.steps[0].id));
}
ngOnDestroy() {
diff --git a/ui/src/app/metadata/provider/container/provider.component.html b/ui/src/app/metadata/provider/container/provider.component.html
index 203223445..8d471248b 100644
--- a/ui/src/app/metadata/provider/container/provider.component.html
+++ b/ui/src/app/metadata/provider/container/provider.component.html
@@ -1,14 +1,3 @@
-
-
+
+
\ No newline at end of file
diff --git a/ui/src/app/metadata/provider/container/provider.component.ts b/ui/src/app/metadata/provider/container/provider.component.ts
index afaf48a27..c23dc7066 100644
--- a/ui/src/app/metadata/provider/container/provider.component.ts
+++ b/ui/src/app/metadata/provider/container/provider.component.ts
@@ -1,9 +1,6 @@
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import * as fromProvider from '../reducer';
-import { SetDefinition, SetIndex } from '../../../wizard/action/wizard.action';
-
-import { MetadataProviderWizard } from '../model';
@Component({
selector: 'provider-page',
@@ -13,8 +10,5 @@ import { MetadataProviderWizard } from '../model';
export class ProviderComponent {
constructor(
private store: Store
- ) {
- this.store.dispatch(new SetDefinition(MetadataProviderWizard));
- this.store.dispatch(new SetIndex(MetadataProviderWizard.steps[0].id));
- }
+ ) {}
}
diff --git a/ui/src/app/metadata/provider/effect/collection.effect.ts b/ui/src/app/metadata/provider/effect/collection.effect.ts
index 1a737839e..d4eb5ff0f 100644
--- a/ui/src/app/metadata/provider/effect/collection.effect.ts
+++ b/ui/src/app/metadata/provider/effect/collection.effect.ts
@@ -1,9 +1,10 @@
import { Injectable } from '@angular/core';
import { Effect, Actions, ofType } from '@ngrx/effects';
+import { Store } from '@ngrx/store';
import { Router } from '@angular/router';
import { of } from 'rxjs';
-import { map, catchError, switchMap, tap } from 'rxjs/operators';
+import { map, catchError, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import {
ProviderCollectionActionsUnion,
ProviderCollectionActionTypes,
@@ -12,9 +13,17 @@ import {
AddProviderFail,
LoadProviderRequest,
LoadProviderSuccess,
- LoadProviderError
+ LoadProviderError,
+ SelectProviderRequest,
+ SelectProviderSuccess,
+ SelectProviderError,
+ UpdateProviderRequest,
+ UpdateProviderSuccess,
+ UpdateProviderFail
} from '../action/collection.action';
import { MetadataProviderService } from '../../domain/service/provider.service';
+import * as fromProvider from '../reducer';
+
/* istanbul ignore next */
@Injectable()
@@ -33,6 +42,20 @@ export class CollectionEffects {
)
);
+ @Effect()
+ selectProviders$ = this.actions$.pipe(
+ ofType(ProviderCollectionActionTypes.SELECT_PROVIDER_REQUEST),
+ map(action => action.payload),
+ switchMap(id =>
+ this.providerService
+ .find(id)
+ .pipe(
+ map(provider => new SelectProviderSuccess({ id, changes: provider })),
+ catchError(error => of(new SelectProviderError(error)))
+ )
+ )
+ );
+
@Effect()
createProvider$ = this.actions$.pipe(
ofType(ProviderCollectionActionTypes.ADD_PROVIDER_REQUEST),
@@ -51,7 +74,37 @@ export class CollectionEffects {
createProviderSuccessRedirect$ = this.actions$.pipe(
ofType(ProviderCollectionActionTypes.ADD_PROVIDER_SUCCESS),
map(action => action.payload),
- tap(provider => this.router.navigate(['metadata']))
+ tap(provider => this.router.navigate(['metadata', 'manager', 'providers']))
+ );
+
+ @Effect()
+ updateProvider$ = this.actions$.pipe(
+ ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_REQUEST),
+ map(action => action.payload),
+ withLatestFrom(this.store.select(fromProvider.getSelectedProvider)),
+ map(([updates, original]) => ({ ...original, ...updates })),
+ switchMap(provider =>
+ this.providerService
+ .update(provider)
+ .pipe(
+ map(p => new UpdateProviderSuccess({id: p.id, changes: p})),
+ catchError((e) => of(new UpdateProviderFail(e)))
+ )
+ )
+ );
+
+ @Effect({ dispatch: false })
+ updateProviderSuccessReload$ = this.actions$.pipe(
+ ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_SUCCESS),
+ map(action => action.payload),
+ tap(provider => this.store.dispatch(new LoadProviderRequest()))
+ );
+
+ @Effect({ dispatch: false })
+ updateProviderSuccessRedirect$ = this.actions$.pipe(
+ ofType(ProviderCollectionActionTypes.UPDATE_PROVIDER_SUCCESS),
+ map(action => action.payload),
+ tap(provider => this.router.navigate(['metadata', 'manager', 'providers']))
);
@Effect()
@@ -64,6 +117,7 @@ export class CollectionEffects {
constructor(
private actions$: Actions,
private router: Router,
+ private store: Store,
private providerService: MetadataProviderService
) { }
} /* istanbul ignore next */
diff --git a/ui/src/app/metadata/provider/effect/entity.effect.ts b/ui/src/app/metadata/provider/effect/entity.effect.ts
new file mode 100644
index 000000000..36f5faa5a
--- /dev/null
+++ b/ui/src/app/metadata/provider/effect/entity.effect.ts
@@ -0,0 +1,29 @@
+import { Injectable } from '@angular/core';
+import { Effect, Actions, ofType } from '@ngrx/effects';
+import { Store } from '@ngrx/store';
+import { map, switchMap } from 'rxjs/operators';
+import { of } from 'rxjs';
+
+import { SelectProviderSuccess, ProviderCollectionActionTypes } from '../action/collection.action';
+
+import * as fromProvider from '../reducer';
+import { MetadataProvider } from '../../domain/model';
+import { UpdateProvider } from '../action/entity.action';
+
+@Injectable()
+export class EntityEffects {
+
+ /*
+ @Effect()
+ loadModelSuccess$ = this.actions$.pipe(
+ ofType(ProviderCollectionActionTypes.SELECT_PROVIDER_SUCCESS),
+ map(action => action.payload.changes),
+ switchMap((provider: MetadataProvider) => of(new UpdateProvider(provider)))
+ );
+ */
+
+ constructor(
+ private store: Store,
+ private actions$: Actions
+ ) { }
+}
diff --git a/ui/src/app/metadata/provider/model/base.provider.form.spec.ts b/ui/src/app/metadata/provider/model/base.provider.form.spec.ts
new file mode 100644
index 000000000..307368ef2
--- /dev/null
+++ b/ui/src/app/metadata/provider/model/base.provider.form.spec.ts
@@ -0,0 +1,142 @@
+import { BaseMetadataProviderEditor } from './base.provider.form';
+
+describe('BaseMetadataProviderForm', () => {
+
+ const parser = BaseMetadataProviderEditor.translate.parser;
+ const formatter = BaseMetadataProviderEditor.translate.formatter;
+
+ const requiredValidUntilFilter = {
+ maxValidityInterval: 1,
+ '@type': 'RequiredValidUntil'
+ };
+
+ const signatureValidationFilter = {
+ requireSignedRoot: true,
+ certificateFile: 'foo',
+ '@type': 'SignatureValidation'
+ };
+
+ const entityRoleWhiteListFilter = {
+ retainedRoles: ['foo', 'bar'],
+ removeRolelessEntityDescriptors: true,
+ removeEmptyEntitiesDescriptors: true,
+ '@type': 'EntityRoleWhiteList'
+ };
+
+ describe('parser', () => {
+ it('should transform the filters object to an array', () => {
+ let model = {
+ name: 'foo',
+ '@type': 'FileBackedHttpMetadataProvider',
+ enabled: true,
+ resourceId: 'foo',
+ metadataFilters: {
+ RequiredValidUntil: requiredValidUntilFilter,
+ SignatureValidation: signatureValidationFilter,
+ EntityRoleWhiteList: entityRoleWhiteListFilter
+ }
+ };
+ expect(
+ parser(model)
+ ).toEqual(
+ {
+ ...model,
+ metadataFilters: [
+ requiredValidUntilFilter,
+ signatureValidationFilter,
+ entityRoleWhiteListFilter
+ ]
+ }
+ );
+ });
+
+ it('should return the object if metadataFilters is not provided', () => {
+ let model = {
+ name: 'foo',
+ '@type': 'FileBackedHttpMetadataProvider',
+ enabled: true,
+ resourceId: 'foo'
+ };
+ expect(
+ parser(model)
+ ).toEqual(
+ model
+ );
+ });
+ });
+
+ describe('formatter', () => {
+ it('should transform the filters object to an array', () => {
+ let model = {
+ name: 'foo',
+ '@type': 'FileBackedHttpMetadataProvider',
+ enabled: true,
+ resourceId: 'foo',
+ metadataFilters: [
+ requiredValidUntilFilter,
+ signatureValidationFilter,
+ entityRoleWhiteListFilter
+ ]
+ };
+ expect(
+ formatter(model)
+ ).toEqual(
+ {
+ ...model,
+ metadataFilters: {
+ RequiredValidUntil: requiredValidUntilFilter,
+ SignatureValidation: signatureValidationFilter,
+ EntityRoleWhiteList: entityRoleWhiteListFilter
+ }
+ }
+ );
+ });
+
+ it('should return the object if metadataFilters is not provided', () => {
+ let model = {
+ name: 'foo',
+ '@type': 'FileBackedHttpMetadataProvider',
+ enabled: true,
+ resourceId: 'foo'
+ };
+ expect(
+ formatter(model)
+ ).toEqual(
+ model
+ );
+ });
+ });
+
+ describe('getValidators', () => {
+ it('should return a set of validator functions for the provider type', () => {
+ const validators = BaseMetadataProviderEditor.getValidators([]);
+ expect(validators).toBeDefined();
+ expect(validators['/']).toBeDefined();
+ expect(validators['/name']).toBeDefined();
+ });
+
+ describe('name `/name` validator', () => {
+ const validators = BaseMetadataProviderEditor.getValidators(['foo', 'bar']);
+
+ it('should return an invalid object when provided values are invalid based on name', () => {
+ expect(validators['/name']('foo', { path: '/name' })).toBeDefined();
+ });
+
+ it('should return null when provided values are valid based on name', () => {
+ expect(validators['/name']('baz', { path: '/name' })).toBeNull();
+ });
+ });
+
+ describe('parent `/` validator', () => {
+ const validators = BaseMetadataProviderEditor.getValidators(['foo', 'bar']);
+
+ it('should return a list of child errors', () => {
+ expect(validators['/']({name: 'foo'}, { path: '/name' }, {}).length).toBe(1);
+ });
+
+ it('should ignore properties that don\'t exist a list of child errors', () => {
+ expect(validators['/']({ foo: 'bar' }, { path: '/foo' }, {})).toBeUndefined();
+ });
+ });
+ });
+});
diff --git a/ui/src/app/metadata/provider/model/base.provider.form.ts b/ui/src/app/metadata/provider/model/base.provider.form.ts
new file mode 100644
index 000000000..a542ebb47
--- /dev/null
+++ b/ui/src/app/metadata/provider/model/base.provider.form.ts
@@ -0,0 +1,60 @@
+import { Wizard } from '../../../wizard/model';
+import { BaseMetadataProvider } from '../../domain/model/providers';
+
+export const BaseMetadataProviderEditor: Wizard = {
+ label: 'BaseMetadataProvider',
+ type: 'BaseMetadataResolver',
+ getValidators(namesList: string[]): any {
+ const validators = {
+ '/': (value, property, form_current) => {
+ let errors;
+ // iterate all customer
+ Object.keys(value).forEach((key) => {
+ const item = value[key];
+ const validatorKey = `/${key}`;
+ const validator = validators.hasOwnProperty(validatorKey) ? validators[validatorKey] : null;
+ const error = validator ? validator(item, { path: `/${key}` }, form_current) : null;
+ if (error) {
+ errors = errors || [];
+ errors.push(error);
+ }
+ });
+ return errors;
+ },
+ '/name': (value, property, form) => {
+ const err = namesList.indexOf(value) > -1 ? {
+ code: 'INVALID_NAME',
+ path: `#${property.path}`,
+ message: 'Name must be unique.',
+ params: [value]
+ } : null;
+ return err;
+ }
+ };
+ return validators;
+ },
+ translate: {
+ parser: (changes: any): BaseMetadataProvider => changes.metadataFilters ? ({
+ ...changes,
+ metadataFilters: [
+ ...Object.keys(changes.metadataFilters).reduce((collection, filterName) => ([
+ ...collection,
+ {
+ ...changes.metadataFilters[filterName],
+ '@type': filterName
+ }
+ ]), [])
+ ]
+ }) : changes,
+ formatter: (changes: BaseMetadataProvider): any => changes.metadataFilters ? ({
+ ...changes,
+ metadataFilters: {
+ ...(changes.metadataFilters || []).reduce((collection, filter) => ({
+ ...collection,
+ [filter['@type']]: filter
+ }), {})
+ }
+ }) : changes
+ },
+ steps: []
+};
diff --git a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts
index 41723be8f..5af8642bb 100644
--- a/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts
+++ b/ui/src/app/metadata/provider/model/file-backed-http.provider.form.ts
@@ -1,32 +1,11 @@
import { Wizard } from '../../../wizard/model';
import { FileBackedHttpMetadataProvider } from '../../domain/model/providers/file-backed-http-metadata-provider';
+import { BaseMetadataProviderEditor } from './base.provider.form';
export const FileBackedHttpMetadataProviderWizard: Wizard = {
+ ...BaseMetadataProviderEditor,
label: 'FileBackedHttpMetadataProvider',
type: 'FileBackedHttpMetadataResolver',
- translate: {
- parser: (changes: any): FileBackedHttpMetadataProvider => changes.metadataFilters ? ({
- ...changes,
- metadataFilters: [
- ...Object.keys(changes.metadataFilters).reduce((collection, filterName) => ([
- ...collection,
- {
- ...changes.metadataFilters[filterName],
- '@type': filterName
- }
- ]), [])
- ]
- }) : changes,
- formatter: (changes: FileBackedHttpMetadataProvider): any => changes.metadataFilters ? ({
- ...changes,
- metadataFilters: {
- ...(changes.metadataFilters || []).reduce((collection, filter) => ({
- ...collection,
- [filter['@type']]: filter
- }), {})
- }
- }) : changes
- },
steps: [
{
id: 'common',
@@ -60,3 +39,45 @@ export const FileBackedHttpMetadataProviderWizard: Wizard = {
+ ...FileBackedHttpMetadataProviderWizard,
+ steps: [
+ {
+ id: 'filter-list',
+ label: 'Filter List',
+ index: 0
+ },
+ {
+ id: 'common',
+ label: 'Common Attributes',
+ index: 1,
+ initialValues: [],
+ schema: 'assets/schema/provider/filebacked-http-common.editor.schema.json'
+ },
+ {
+ id: 'reloading',
+ label: 'Reloading Attributes',
+ index: 2,
+ initialValues: [],
+ schema: 'assets/schema/provider/filebacked-http-reloading.schema.json'
+ },
+ {
+ id: 'filters',
+ label: 'Metadata Filter Plugins',
+ index: 3,
+ initialValues: [
+ { key: 'metadataFilters', value: [] }
+ ],
+ schema: 'assets/schema/provider/filebacked-http-filters.schema.json'
+ },
+ {
+ id: 'advanced',
+ label: 'Advanced Settings',
+ index: 4,
+ initialValues: [],
+ schema: 'assets/schema/provider/filebacked-http-advanced.schema.json'
+ }
+ ]
+};
diff --git a/ui/src/app/metadata/provider/model/index.ts b/ui/src/app/metadata/provider/model/index.ts
index 508650ca7..a6c438df4 100644
--- a/ui/src/app/metadata/provider/model/index.ts
+++ b/ui/src/app/metadata/provider/model/index.ts
@@ -1,8 +1,15 @@
import { FileBackedHttpMetadataProviderWizard } from './file-backed-http.provider.form';
+import { FileBackedHttpMetadataProviderEditor } from './file-backed-http.provider.form';
+import { BaseMetadataProviderEditor } from './base.provider.form';
-export const MetadataProviderTypes = [
+export const MetadataProviderWizardTypes = [
FileBackedHttpMetadataProviderWizard
];
+export const MetadataProviderEditorTypes = [
+ FileBackedHttpMetadataProviderEditor,
+ BaseMetadataProviderEditor
+];
+
export * from './file-backed-http.provider.form';
export * from './provider.form';
diff --git a/ui/src/app/metadata/provider/model/provider.form.ts b/ui/src/app/metadata/provider/model/provider.form.ts
index 698d20134..f7e0ae773 100644
--- a/ui/src/app/metadata/provider/model/provider.form.ts
+++ b/ui/src/app/metadata/provider/model/provider.form.ts
@@ -1,14 +1,12 @@
import { Wizard, WizardStep } from '../../../wizard/model';
import { MetadataProvider } from '../../domain/model';
import { Metadata } from '../../domain/domain.type';
+import { BaseMetadataProviderEditor } from './base.provider.form';
export const MetadataProviderWizard: Wizard = {
+ ...BaseMetadataProviderEditor,
label: 'MetadataProvider',
type: 'MetadataProvider',
- translate: {
- parser: changes => changes,
- formatter: model => model
- },
steps: [
{
id: 'new',
diff --git a/ui/src/app/metadata/provider/provider.module.ts b/ui/src/app/metadata/provider/provider.module.ts
index 104bae13d..288adf30b 100644
--- a/ui/src/app/metadata/provider/provider.module.ts
+++ b/ui/src/app/metadata/provider/provider.module.ts
@@ -19,7 +19,12 @@ import { CustomWidgetRegistry } from '../../schema-form/registry';
import { SummaryPropertyComponent } from './component/summary-property.component';
import { CollectionEffects } from './effect/collection.effect';
import { SharedModule } from '../../shared/shared.module';
-
+import { ProviderEditComponent } from './container/provider-edit.component';
+import { ProviderSelectComponent } from './container/provider-select.component';
+import { ProviderEditStepComponent } from './container/provider-edit-step.component';
+import { EntityEffects } from './effect/entity.effect';
+import { ProviderFilterListComponent } from './container/provider-filter-list.component';
+import { NgbDropdownModule } from '../../../../node_modules/@ng-bootstrap/ng-bootstrap';
@NgModule({
declarations: [
@@ -27,6 +32,10 @@ import { SharedModule } from '../../shared/shared.module';
ProviderWizardComponent,
ProviderWizardStepComponent,
ProviderWizardSummaryComponent,
+ ProviderEditComponent,
+ ProviderEditStepComponent,
+ ProviderSelectComponent,
+ ProviderFilterListComponent,
SummaryPropertyComponent
],
entryComponents: [],
@@ -36,7 +45,8 @@ import { SharedModule } from '../../shared/shared.module';
WizardModule,
RouterModule,
SharedModule,
- FormModule
+ FormModule,
+ NgbDropdownModule
],
exports: []
})
@@ -55,7 +65,7 @@ export class ProviderModule {
imports: [
ProviderModule,
StoreModule.forFeature('provider', fromProvider.reducers),
- EffectsModule.forFeature([EditorEffects, CollectionEffects])
+ EffectsModule.forFeature([EntityEffects, EditorEffects, CollectionEffects])
]
})
export class RootProviderModule { }
diff --git a/ui/src/app/metadata/provider/provider.routing.ts b/ui/src/app/metadata/provider/provider.routing.ts
index 98cdddd85..cf491a92f 100644
--- a/ui/src/app/metadata/provider/provider.routing.ts
+++ b/ui/src/app/metadata/provider/provider.routing.ts
@@ -3,6 +3,10 @@ import { Routes } from '@angular/router';
import { ProviderComponent } from './container/provider.component';
import { ProviderWizardComponent } from './container/provider-wizard.component';
import { ProviderWizardStepComponent } from './container/provider-wizard-step.component';
+import { ProviderEditComponent } from './container/provider-edit.component';
+import { ProviderEditStepComponent } from './container/provider-edit-step.component';
+import { ProviderSelectComponent } from './container/provider-select.component';
+import { ProviderFilterListComponent } from './container/provider-filter-list.component';
export const ProviderRoutes: Routes = [
{
@@ -11,8 +15,7 @@ export const ProviderRoutes: Routes = [
children: [
{
path: 'wizard',
- redirectTo: `wizard/new`,
- pathMatch: 'prefix'
+ redirectTo: `wizard/new`
},
{
path: 'wizard',
@@ -24,6 +27,26 @@ export const ProviderRoutes: Routes = [
component: ProviderWizardStepComponent
}
]
+ },
+ {
+ path: ':providerId',
+ component: ProviderSelectComponent,
+ children: [
+ {
+ path: 'edit',
+ component: ProviderEditComponent,
+ children: [
+ {
+ path: 'filter-list',
+ component: ProviderFilterListComponent
+ },
+ {
+ path: ':form',
+ component: ProviderEditStepComponent
+ }
+ ]
+ }
+ ]
}
]
}
diff --git a/ui/src/app/metadata/provider/reducer/collection.reducer.ts b/ui/src/app/metadata/provider/reducer/collection.reducer.ts
index 6bc1af8e7..8cc0c6f0c 100644
--- a/ui/src/app/metadata/provider/reducer/collection.reducer.ts
+++ b/ui/src/app/metadata/provider/reducer/collection.reducer.ts
@@ -23,6 +23,13 @@ export const initialState: CollectionState = adapter.getInitialState({
export function reducer(state = initialState, action: ProviderCollectionActionsUnion): CollectionState {
switch (action.type) {
+ case ProviderCollectionActionTypes.SELECT_PROVIDER_SUCCESS: {
+ return adapter.upsertOne(action.payload, {
+ ...state,
+ selectedProviderId: action.payload.id.toString()
+ });
+ }
+
case ProviderCollectionActionTypes.LOAD_PROVIDER_SUCCESS: {
return adapter.addAll(action.payload, {
...state,
diff --git a/ui/src/app/metadata/provider/reducer/entity.reducer.ts b/ui/src/app/metadata/provider/reducer/entity.reducer.ts
index 5aa7eceb1..d5b63eca1 100644
--- a/ui/src/app/metadata/provider/reducer/entity.reducer.ts
+++ b/ui/src/app/metadata/provider/reducer/entity.reducer.ts
@@ -3,13 +3,11 @@ import { EntityActionTypes, EntityActionUnion } from '../action/entity.action';
export interface EntityState {
saving: boolean;
- base: MetadataProvider;
changes: MetadataProvider;
}
export const initialState: EntityState = {
saving: false,
- base: null,
changes: null
};
@@ -28,14 +26,6 @@ export function reducer(state = initialState, action: EntityActionUnion): Entity
}
};
}
- case EntityActionTypes.SELECT_PROVIDER: {
- return {
- ...state,
- base: {
- ...action.payload
- }
- };
- }
case EntityActionTypes.UPDATE_PROVIDER: {
return {
...state,
@@ -54,4 +44,4 @@ export function reducer(state = initialState, action: EntityActionUnion): Entity
export const isEntitySaved = (state: EntityState) => !Object.keys(state.changes).length && !state.saving;
export const getEntityChanges = (state: EntityState) => state.changes;
export const isEditorSaving = (state: EntityState) => state.saving;
-export const getUpdatedEntity = (state: EntityState) => ({ ...state.base, ...state.changes });
+export const getUpdatedEntity = (state: EntityState) => state.changes;
diff --git a/ui/src/app/wizard/model/wizard.ts b/ui/src/app/wizard/model/wizard.ts
index 8b5fe344e..3c9477f5a 100644
--- a/ui/src/app/wizard/model/wizard.ts
+++ b/ui/src/app/wizard/model/wizard.ts
@@ -6,13 +6,15 @@ export interface Wizard {
parser(changes: Partial, schema?: any),
formatter(changes: Partial, schema?: any)
};
+
+ getValidators?(params: any): { [key: string]: any };
}
export interface WizardStep {
id: string;
label: string;
initialValues?: WizardValue[];
- schema: string;
+ schema?: string;
index: number;
}
diff --git a/ui/src/app/wizard/reducer/index.spec.ts b/ui/src/app/wizard/reducer/index.spec.ts
index 78ff09441..9c8b246f9 100644
--- a/ui/src/app/wizard/reducer/index.spec.ts
+++ b/ui/src/app/wizard/reducer/index.spec.ts
@@ -84,7 +84,6 @@ describe('wizard index selectors', () => {
describe('getModelFn method', () => {
it('should return the model', () => {
const step = FileBackedHttpMetadataProviderWizard.steps.find(s => s.id === 'filters');
- console.log(step);
expect(selectors.getModelFn(step)).toEqual({ metadataFilters: [] });
});
});
diff --git a/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json b/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json
new file mode 100644
index 000000000..601aae3db
--- /dev/null
+++ b/ui/src/assets/schema/provider/filebacked-http-advanced.schema.json
@@ -0,0 +1,6 @@
+{
+ "type": "object",
+ "title": "Advanced Settings",
+ "order": [],
+ "properties": {}
+}
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
new file mode 100644
index 000000000..77590af65
--- /dev/null
+++ b/ui/src/assets/schema/provider/filebacked-http-common.editor.schema.json
@@ -0,0 +1,244 @@
+{
+ "type": "object",
+ "order": [
+ "name",
+ "@type",
+ "id",
+ "metadataURL",
+ "initializeFromBackupFile",
+ "backingFile",
+ "backupFileInitNextRefreshDelay",
+ "requireValidMetadata",
+ "failFastInitialization",
+ "useDefaultPredicateRegistry",
+ "satisfyAnyPredicates"
+ ],
+ "required": [
+ "id",
+ "metadataURL"
+ ],
+ "anyOf": [
+ {
+ "properties": {
+ "initializeFromBackupFile": {
+ "enum": [
+ true
+ ]
+ }
+ },
+ "required": [
+ "backingFile"
+ ]
+ },
+ {
+ "properties": {
+ "initializeFromBackupFile": {
+ "enum": [
+ false
+ ]
+ }
+ }
+ }
+ ],
+ "fieldsets": [
+ {
+ "type": "section",
+ "fields": [
+ "name",
+ "@type"
+ ]
+ },
+ {
+ "fields": [
+ "id",
+ "metadataURL",
+ "initializeFromBackupFile",
+ "backingFile",
+ "backupFileInitNextRefreshDelay",
+ "requireValidMetadata",
+ "failFastInitialization",
+ "useDefaultPredicateRegistry",
+ "satisfyAnyPredicates"
+ ]
+ }
+ ],
+ "properties": {
+ "name": {
+ "title": "Metadata Provider Name (Dashboard Display Only)",
+ "description": "Metadata Provider Name",
+ "type": "string",
+ "widget": {
+ "id": "string",
+ "help": "Must be unique."
+ }
+ },
+ "@type": {
+ "title": "Metadata Provider Type",
+ "description": "Metadata Provider Type",
+ "placeholder": "Select a metadata provider type",
+ "type": "string",
+ "readOnly": true,
+ "widget": {
+ "id": "select"
+ },
+ "oneOf": [
+ {
+ "enum": [
+ "FileBackedHttpMetadataResolver"
+ ],
+ "description": "FileBackedHttpMetadataProvider"
+ }
+ ]
+ },
+ "id": {
+ "title": "ID",
+ "description": "Identifier for logging, identification for command line reload, etc.",
+ "type": "string",
+ "default": "",
+ "minLength": 1
+ },
+ "metadataURL": {
+ "title": "Metadata URL",
+ "description": "Identifier for logging, identification for command line reload, etc.",
+ "type": "string",
+ "default": "",
+ "minLength": 1
+ },
+ "initializeFromBackupFile": {
+ "title": "Initialize From Backup File?",
+ "description": "Flag indicating whether initialization should first attempt to load metadata from the backup file. If true, foreground initialization will be performed by loading the backing file, and then a refresh from the remote HTTP server will be scheduled to execute in a background thread, after a configured delay. This can improve IdP startup times when the remote HTTP file is large in size.",
+ "type": "boolean",
+ "widget": {
+ "id": "boolean-radio"
+ },
+ "oneOf": [
+ {
+ "enum": [
+ true
+ ],
+ "description": "True"
+ },
+ {
+ "enum": [
+ false
+ ],
+ "description": "False"
+ }
+ ],
+ "default": true
+ },
+ "backingFile": {
+ "title": "Backing File",
+ "description": "Specifies where the backing file is located. If the remote server is unavailable at startup, the backing file is loaded instead.",
+ "type": "string",
+ "default": "",
+ "visibleIf": {
+ "initializeFromBackupFile": [
+ true
+ ]
+ }
+ },
+ "backupFileInitNextRefreshDelay": {
+ "title": "Backup File Init Next Refresh Delay",
+ "description": "Delay duration after which to schedule next HTTP refresh when initialized from the backing file.",
+ "type": "string",
+ "visibleIf": {
+ "initializeFromBackupFile": [
+ true
+ ]
+ }
+ },
+ "requireValidMetadata": {
+ "title": "Require Valid Metadata?",
+ "description": "Whether candidate metadata found by the resolver must be valid in order to be returned (where validity is implementation specific, but in SAML cases generally depends on a validUntil attribute.) If this flag is true, then invalid candidate metadata will not be returned.",
+ "type": "boolean",
+ "widget": {
+ "id": "boolean-radio"
+ },
+ "oneOf": [
+ {
+ "enum": [
+ true
+ ],
+ "description": "True"
+ },
+ {
+ "enum": [
+ false
+ ],
+ "description": "False"
+ }
+ ],
+ "default": true
+ },
+ "failFastInitialization": {
+ "title": "Fail Fast Initialization?",
+ "description": "Whether to fail initialization of the underlying MetadataResolverService (and possibly the IdP as a whole) if the initialization of a metadata provider fails. When false, the IdP may start, and will continue to attempt to reload valid metadata if configured to do so, but operations that require valid metadata will fail until it does.",
+ "type": "boolean",
+ "widget": {
+ "id": "boolean-radio"
+ },
+ "oneOf": [
+ {
+ "enum": [
+ true
+ ],
+ "description": "True"
+ },
+ {
+ "enum": [
+ false
+ ],
+ "description": "False"
+ }
+ ],
+ "default": true
+ },
+ "useDefaultPredicateRegistry": {
+ "title": "Use Default Predicate Registry?",
+ "description": "Whether to fail initialization of the underlying MetadataResolverService (and possibly the IdP as a whole) if the initialization of a metadata provider fails. When false, the IdP may start, and will continue to attempt to reload valid metadata if configured to do so, but operations that require valid metadata will fail until it does.",
+ "type": "boolean",
+ "widget": {
+ "id": "boolean-radio"
+ },
+ "oneOf": [
+ {
+ "enum": [
+ true
+ ],
+ "description": "True"
+ },
+ {
+ "enum": [
+ false
+ ],
+ "description": "False"
+ }
+ ],
+ "default": true
+ },
+ "satisfyAnyPredicates": {
+ "title": "Satisy Any Predicates?",
+ "description": "Flag which determines whether predicates used in filtering are connected by a logical 'OR' (true) or by logical 'AND' (false).",
+ "type": "boolean",
+ "widget": {
+ "id": "boolean-radio"
+ },
+ "oneOf": [
+ {
+ "enum": [
+ true
+ ],
+ "description": "True"
+ },
+ {
+ "enum": [
+ false
+ ],
+ "description": "False"
+ }
+ ],
+ "default": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/ui/src/testing/activated-route.stub.ts b/ui/src/testing/activated-route.stub.ts
index 511b78744..f41853bf9 100644
--- a/ui/src/testing/activated-route.stub.ts
+++ b/ui/src/testing/activated-route.stub.ts
@@ -2,7 +2,7 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
-import { convertToParamMap, ParamMap } from '@angular/router';
+import { convertToParamMap, ParamMap, ActivatedRoute } from '@angular/router';
@Injectable()
export class ActivatedRouteStub {
@@ -13,6 +13,9 @@ export class ActivatedRouteStub {
// Test parameters
private _testParamMap: ParamMap;
+
+ private _firstChild: ActivatedRouteStub;
+
get testParamMap() { return this._testParamMap; }
set testParamMap(params: {}) {
this._testParamMap = convertToParamMap(params);
@@ -27,4 +30,12 @@ export class ActivatedRouteStub {
get params() {
return this.paramMap;
}
+
+ get firstChild(): ActivatedRouteStub {
+ return this._firstChild;
+ }
+
+ set firstChild(stub: ActivatedRouteStub) {
+ this._firstChild = stub;
+ }
}