diff --git a/backend/src/integration/resources/SHIBUI-1333.side b/backend/src/integration/resources/SHIBUI-1333.side index 250660ef8..78143b150 100644 --- a/backend/src/integration/resources/SHIBUI-1333.side +++ b/backend/src/integration/resources/SHIBUI-1333.side @@ -2045,13 +2045,6 @@ ["xpath=//section[4]/div/div[2]/object-property/array-property/div/div[3]/div/div[2]", "xpath:position"] ], "value": "http://test.org/logout2" - }, { - "id": "e1cba732-dd34-4c73-9d4c-89c440975dd8", - "comment": "", - "command": "runScript", - "target": "window.scrollTo(0,2060)", - "targets": [], - "value": "" }, { "id": "fe47ffd2-d2a0-4264-90fe-31b998f81ac2", "comment": "", diff --git a/backend/src/integration/resources/SHIBUI-1334-1.side b/backend/src/integration/resources/SHIBUI-1334-1.side index fdfacc02e..7ffb79675 100644 --- a/backend/src/integration/resources/SHIBUI-1334-1.side +++ b/backend/src/integration/resources/SHIBUI-1334-1.side @@ -2045,13 +2045,6 @@ ["xpath=//section[4]/div/div[2]/object-property/array-property/div/div[3]/div/div[2]", "xpath:position"] ], "value": "http://test.org/logout2" - }, { - "id": "e1cba732-dd34-4c73-9d4c-89c440975dd8", - "comment": "", - "command": "runScript", - "target": "window.scrollTo(0,2060)", - "targets": [], - "value": "" }, { "id": "fe47ffd2-d2a0-4264-90fe-31b998f81ac2", "comment": "", @@ -2285,20 +2278,13 @@ ["xpath=//span[contains(.,'Test Provider')]", "xpath:innerText"] ], "value": "Test Provider" - }, { - "id": "d3abb044-064c-4d68-8afa-84cc5f9b987e", - "comment": "", - "command": "runScript", - "target": "window.scrollTo(0,157)", - "targets": [], - "value": "" }, { "id": "138ad58b-f0a2-436b-a8b0-43484f4180e6", "comment": "", "command": "assertText", - "target": "css=array-property:nth-child(5) .py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(2)", + "target": "css=.bg-diff > .py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(3)", "targets": [ - ["css=array-property:nth-child(5) .py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(2)", "css:finder"], + ["css=.bg-diff > .py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(3)", "css:finder"], ["xpath=//div[2]/div/div[2]", "xpath:position"] ], "value": "Test User v2" @@ -2306,10 +2292,10 @@ "id": "17049c95-4724-4521-ac47-6b64bde822aa", "comment": "", "command": "assertText", - "target": "css=array-property:nth-child(5) .py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(3)", + "target": "css=.py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(4)", "targets": [ - ["css=array-property:nth-child(5) .py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(3)", "css:finder"], - ["xpath=//div[3]", "xpath:position"] + ["css=.py-2:nth-child(2) > .d-flex:nth-child(1) > .text-truncate:nth-child(4)", "css:finder"], + ["xpath=//div/div[3]", "xpath:position"] ], "value": "Test User" }, { diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 5640fd5de..ff6eb36d2 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -56,6 +56,7 @@ action.manage=Manage action.close=Close action.back-to-top=Back to Top action.restore=Restore +action.view-only-changes=View Only Changes value.enabled=Enabled value.disabled=Disabled diff --git a/ui/src/app/metadata/configuration/action/compare.action.ts b/ui/src/app/metadata/configuration/action/compare.action.ts index 410a8dcab..be687d142 100644 --- a/ui/src/app/metadata/configuration/action/compare.action.ts +++ b/ui/src/app/metadata/configuration/action/compare.action.ts @@ -8,7 +8,8 @@ export enum CompareActionTypes { COMPARE_METADATA_SUCCESS = '[Compare Version] Compare Version Success', COMPARE_METADATA_ERROR = '[Compare Version] Compare Version Error', SET_VERSIONS = '[Compare Version] Set Versions', - CLEAR_VERSIONS = '[Compare Version] Clear Versions' + CLEAR_VERSIONS = '[Compare Version] Clear Versions', + SET_VIEW_CHANGED = '[Compare Version] View Changed Only' } export class CompareVersionRequest implements Action { @@ -35,6 +36,11 @@ export class SetMetadataVersions implements Action { constructor(public payload: Metadata[]) { } } +export class ViewChanged implements Action { + readonly type = CompareActionTypes.SET_VIEW_CHANGED; + constructor(public payload: boolean) { } +} + export class ClearVersions implements Action { readonly type = CompareActionTypes.CLEAR_VERSIONS; } @@ -44,4 +50,5 @@ export type CompareActionsUnion = | CompareVersionSuccess | CompareVersionError | SetMetadataVersions - | ClearVersions; + | ClearVersions + | ViewChanged; 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 f4826cfe6..e0685e743 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html @@ -18,20 +18,26 @@

-
- Option - - Value - {{ date | date:DATE_FORMAT }} - -
- - + +
+ Option + + Value + {{ date | date:DATE_FORMAT }} + +
+ + +
+ +
+ No Changes +
+
diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss b/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss new file mode 100644 index 000000000..82e7a5f37 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss @@ -0,0 +1,20 @@ +@import '../../../../theme/_palette'; + +:host /deep/ { + .bg-diff { + background: #FEE8E9; + position: relative; + border-color: #CCC !important; + &::before { + $size: 24px; + content: "\f06a"; + font-family: 'FontAwesome'; + text-align: center; + width: $size; + height: $size; + position: absolute; + left: -$size; + top: 0.5rem; + } + } +} 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 280c362a6..1b04ba636 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts @@ -8,7 +8,7 @@ import { CONFIG_DATE_FORMAT } from '../configuration.values'; selector: 'metadata-configuration', changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: './metadata-configuration.component.html', - styleUrls: [] + styleUrls: ['./metadata-configuration.component.scss'] }) export class MetadataConfigurationComponent { @Input() configuration: MetadataConfiguration; diff --git a/ui/src/app/metadata/configuration/component/property/array-property.component.html b/ui/src/app/metadata/configuration/component/property/array-property.component.html index e16eb8e65..bfda5c422 100644 --- a/ui/src/app/metadata/configuration/component/property/array-property.component.html +++ b/ui/src/app/metadata/configuration/component/property/array-property.component.html @@ -1,58 +1,64 @@ -
- -
{{ property.name }}
-
-
-
- {{ property.items.properties[prop].title }} -
- -
- {{ version[i][prop] }} -
-
- - -
-
+
+
{{ property.name }}
+
+
+ Changed: +
+ {{ property.items.properties[prop].title }}
+ +
+ {{ version[i][prop] }} +
+
+ - +
+
+
+
+ + + - - - - - + + + + +
- - -
- -
-
-
- {{ attr.label }} -
- - true - - - false - -
+
+
+
+ Changed: + {{ item.label }} +
+ + true + + + false +
- +
-
+
-
- {{ property.name }} +
+ Changed: + {{ property.name }}

-

    { describe('attributeList$ getter', () => { it('should return an empty list when no data or dataUrl is set', () => { - app.attributeList$.subscribe((list) => { - expect(list).toEqual([]); - }); + expect(app.dataList).toBeUndefined(); }); it('should return a list of data items from the schema', () => { const datalist = [ @@ -87,29 +85,7 @@ describe('Array Property Component', () => { } }); 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); - }); + expect(app.dataList).toEqual(datalist); }); }); }); diff --git a/ui/src/app/metadata/configuration/component/property/array-property.component.ts b/ui/src/app/metadata/configuration/component/property/array-property.component.ts index 2c4de49bd..4d09d9a8f 100644 --- a/ui/src/app/metadata/configuration/component/property/array-property.component.ts +++ b/ui/src/app/metadata/configuration/component/property/array-property.component.ts @@ -33,14 +33,8 @@ export class ArrayPropertyComponent extends ConfigurationPropertyComponent imple return UriValidator.isUri(str); } - 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([]); + get dataList(): { key: string, label: string, differences?: boolean }[] { + return this.property.widget.data; } } diff --git a/ui/src/app/metadata/configuration/component/property/primitive-property.component.html b/ui/src/app/metadata/configuration/component/property/primitive-property.component.html index 4d93c07cc..5e582350d 100644 --- a/ui/src/app/metadata/configuration/component/property/primitive-property.component.html +++ b/ui/src/app/metadata/configuration/component/property/primitive-property.component.html @@ -1,4 +1,8 @@ -
    +
    +Changed: +
    {{ v ? v : (v === false) ? v : '-' }} +
    \ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html index 8e652d1c2..5c93f5ec7 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html @@ -8,13 +8,23 @@

    'container-fluid': (numVersions$ | async) > 2, 'container': (numVersions$ | async) <= 2 }"> -
    +
      Version History +
    - +
    diff --git a/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts b/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts index 1b1834fa1..06f821633 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts @@ -1,10 +1,10 @@ import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, BehaviorSubject, Subscription, combineLatest } from 'rxjs'; import { Store } from '@ngrx/store'; import { ActivatedRoute } from '@angular/router'; -import { map } from 'rxjs/operators'; -import { ConfigurationState, getComparisonConfigurations, getComparisonConfigurationCount } from '../reducer'; -import { CompareVersionRequest, ClearVersions } from '../action/compare.action'; +import { map, withLatestFrom } from 'rxjs/operators'; +import { ConfigurationState, getComparisonConfigurationCount } from '../reducer'; +import { CompareVersionRequest, ClearVersions, ViewChanged } from '../action/compare.action'; import { MetadataConfiguration } from '../model/metadata-configuration'; import * as fromReducer from '../reducer'; @@ -16,10 +16,14 @@ import * as fromReducer from '../reducer'; }) export class MetadataComparisonComponent implements OnDestroy { + limiter: BehaviorSubject = new BehaviorSubject(false); + versions$: Observable; numVersions$: Observable; type$: Observable; loading$: Observable = this.store.select(fromReducer.getComparisonLoading); + limited$: Observable = this.store.select(fromReducer.getViewChangedOnly); + sub: Subscription; constructor( private store: Store, @@ -31,12 +35,20 @@ export class MetadataComparisonComponent implements OnDestroy { map(versions => new CompareVersionRequest(versions)) ).subscribe(this.store); - this.versions$ = this.store.select(getComparisonConfigurations); + this.versions$ = this.store.select(fromReducer.getLimitedComparisonConfigurations); this.numVersions$ = this.store.select(getComparisonConfigurationCount); this.type$ = this.store.select(fromReducer.getConfigurationModelType); + + this.versions$.subscribe(console.log); + + this.sub = this.limiter.pipe( + withLatestFrom(this.limited$), + map(([compare, limit]) => new ViewChanged(!limit)) + ).subscribe(this.store); } ngOnDestroy(): void { + this.sub.unsubscribe(); this.store.dispatch(new ClearVersions()); } } diff --git a/ui/src/app/metadata/configuration/model/section.ts b/ui/src/app/metadata/configuration/model/section.ts index fd7f9b00f..50c6a5492 100644 --- a/ui/src/app/metadata/configuration/model/section.ts +++ b/ui/src/app/metadata/configuration/model/section.ts @@ -4,14 +4,18 @@ export interface Section { label: string; pageNumber: number; properties: SectionProperty[]; + differences?: boolean; } export interface SectionProperty { label: string; type: string; value: any[]; + differences?: boolean; + properties?: SectionProperty[]; widget?: { id: string; + data?: any[]; [propertyName: string]: any; }; } diff --git a/ui/src/app/metadata/configuration/reducer/compare.reducer.ts b/ui/src/app/metadata/configuration/reducer/compare.reducer.ts index d2d346ca3..84eec1558 100644 --- a/ui/src/app/metadata/configuration/reducer/compare.reducer.ts +++ b/ui/src/app/metadata/configuration/reducer/compare.reducer.ts @@ -5,16 +5,23 @@ export interface State { models: Metadata[]; loaded: boolean; loading: boolean; + compareChangedOnly: boolean; } export const initialState: State = { models: [], loaded: false, - loading: false + loading: false, + compareChangedOnly: false }; export function reducer(state = initialState, action: CompareActionsUnion): State { switch (action.type) { + case CompareActionTypes.SET_VIEW_CHANGED: + return { + ...state, + compareChangedOnly: action.payload + }; case CompareActionTypes.COMPARE_METADATA_REQUEST: return { ...state, @@ -45,3 +52,4 @@ export function reducer(state = initialState, action: CompareActionsUnion): Stat export const getVersionModels = (state: State) => state.models; export const getVersionModelsLoaded = (state: State) => state.loaded; export const getComparisonLoading = (state: State) => state.loading; +export const getViewChangedOnly = (state: State) => state.compareChangedOnly; diff --git a/ui/src/app/metadata/configuration/reducer/index.spec.ts b/ui/src/app/metadata/configuration/reducer/index.spec.ts index eba8409be..8a1aa3cb7 100644 --- a/ui/src/app/metadata/configuration/reducer/index.spec.ts +++ b/ui/src/app/metadata/configuration/reducer/index.spec.ts @@ -1,19 +1,89 @@ import { getConfigurationSectionsFn, getConfigurationModelNameFn, - getConfigurationModelEnabledFn + getConfigurationModelEnabledFn, + assignValueToProperties, + getLimitedPropertiesFn, + getConfigurationModelTypeFn, + getSelectedVersionNumberFn, + getSelectedIsCurrentFn } from './index'; import { SCHEMA as schema } from '../../../../testing/form-schema.stub'; -import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; import { Metadata } from '../../domain/domain.type'; +import { MockMetadataWizard } from '../../../../testing/mockMetadataWizard'; describe('Configuration Reducer', () => { const model = { name: 'foo', - '@type': 'MetadataResolver' + serviceEnabled: true, + foo: { + bar: 'bar', + baz: 'baz' + }, + list: [ + 'super', + 'cool' + ] }; - const definition = new MetadataSourceEditor(); + const props = [ + { + id: 'name', + items: null, + name: 'label.metadata-provider-name-dashboard-display-only', + properties: [], + type: 'string', + value: null, + widget: { id: 'string', help: 'message.must-be-unique' } + }, + { + id: 'serviceEnabled', + items: null, + name: 'serviceEnabled', + properties: [], + type: 'string', + value: null, + widget: { id: 'select', disabled: true } + }, + { + id: 'foo', + items: null, + name: 'foo', + type: 'object', + properties: [ + { + id: 'bar', + name: 'bar', + type: 'string', + properties: [] + }, + { + id: 'baz', + name: 'baz', + type: 'string', + properties: [] + } + ] + }, + { + id: 'list', + name: 'list', + type: 'array', + items: { + type: 'string' + }, + widget: { + id: 'datalist', + data: [ + { key: 'super', label: 'super' }, + { key: 'cool', label: 'cool' }, + { key: 'notcool', label: 'notcool' } + ] + } + } + ]; + + const definition = MockMetadataWizard; describe('getConfigurationSectionsFn', () => { it('should parse the schema, definition, and model into a MetadataConfiguration', () => { @@ -37,4 +107,62 @@ describe('Configuration Reducer', () => { expect(getConfigurationModelEnabledFn(null)).toBe(false); }); }); + + describe('assignValueToProperties function', () => { + it('should assign appropriate values to the given schema properties', () => { + const assigned = assignValueToProperties([model], props, definition); + expect(assigned[0].value).toEqual(['foo']); + expect(assigned[1].value).toEqual([true]); + }); + + it('should assign differences when passed multiple models', () => { + const assigned = assignValueToProperties([model, { + ...model, + name: 'bar', + list: [ + 'super', + 'notcool' + ] + }], props, definition); + expect(assigned[0].differences).toBe(true); + }); + }); + + describe('getLimitedPropertiesFn function', () => { + it('should filter properties without differences', () => { + const assigned = assignValueToProperties([model, { + ...model, + name: 'bar' + }], props, definition); + expect(getLimitedPropertiesFn(assigned).length).toBe(1); + }); + }); + + describe('getConfigurationModelTypeFn function ', () => { + it('should return provider type if the object has an @type property', () => { + const md = { '@type': 'FilebackedHttpMetadataResolver' } as Metadata; + expect(getConfigurationModelTypeFn(md)).toBe('FilebackedHttpMetadataResolver'); + }); + it('should return resolver if no type is detected', () => { + const md = { serviceEnabled: true } as Metadata; + expect(getConfigurationModelTypeFn(md)).toBe('resolver'); + }); + }); + + describe('getSelectedVersionNumberFn function ', () => { + it('should return the selected version by id', () => { + const versions = [ { id: 'foo' }, { id: 'bar' } ]; + const id = 'foo'; + expect(getSelectedVersionNumberFn(versions, id)).toBe(1); + }); + }); + + describe('getSelectedIsCurrentFn function ', () => { + it('should return a boolean of whether the selected version is the most current version', () => { + const versions = [{ id: 'foo' }, { id: 'bar' }]; + const id = 'foo'; + expect(getSelectedIsCurrentFn(versions[0], versions)).toBe(true); + expect(getSelectedIsCurrentFn(versions[1], versions)).toBe(false); + }); + }); }); diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index 62ecb8404..38087a034 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -16,6 +16,7 @@ import { Metadata } from '../../domain/domain.type'; import * as fromResolver from '../../resolver/reducer'; import * as fromProvider from '../../provider/reducer'; +import { SectionProperty } from '../model/section'; export interface ConfigurationState { configuration: fromConfiguration.State; @@ -55,10 +56,31 @@ export const getConfigurationXml = createSelector(getConfigurationState, fromCon export const assignValueToProperties = (models, properties, definition: any): any[] => { return properties.map(prop => { + const differences = models.some((model, index, array) => { + if (!array) { + return false; + } + return JSON.stringify(model[prop.id]) !== JSON.stringify(array[0][prop.id]); + }); + + const widget = prop.type === 'array' && prop.widget && prop.widget.data ? ({ + ...prop.widget, + data: prop.widget.data.map(item => ({ + ...item, + differences: models + .map((model) => { + const value = model[prop.id]; + return value ? value.indexOf(item.key) > -1 : false; + }) + .reduce((current, val) => current !== val ? true : false, false) + })) + }) : null; + switch (prop.type) { case 'object': return { ...prop, + differences, properties: assignValueToProperties( models.map(model => definition.formatter(model)[prop.id] || {}), prop.properties, @@ -68,9 +90,11 @@ export const assignValueToProperties = (models, properties, definition: any): an default: return { ...prop, + differences, value: models.map(model => { return model[prop.id]; - }) + }), + widget }; } }); @@ -103,6 +127,10 @@ export const getConfigurationSectionsFn = (models, definition, schema): Metadata properties: assignValueToProperties(models, section.properties, definition) }; }) + .map((section: any) => ({ + ...section, + differences: section.properties.some(prop => prop.differences) + })) }); }; @@ -127,18 +155,21 @@ export const getSelectedVersionId = createSelector(getHistoryState, fromHistory. export const getVersionIds = createSelector(getHistoryState, fromHistory.selectVersionIds); export const getVersionCollection = createSelector(getHistoryState, getVersionIds, fromHistory.selectAllVersions); export const getSelectedVersion = createSelector(getVersionEntities, getSelectedVersionId, getInCollectionFn); +export const getSelectedVersionNumberFn = (versions, selectedId) => versions.indexOf(versions.find(v => v.id === selectedId)) + 1; export const getSelectedVersionNumber = createSelector( getVersionCollection, getSelectedVersionId, - (versions, selectedId) => versions.indexOf(versions.find(v => v.id === selectedId)) + 1 + getSelectedVersionNumberFn ); +export const getSelectedIsCurrentFn = (selected, collection) => { + return selected ? collection[0].id === selected.id : false; +}; + export const getSelectedIsCurrent = createSelector( getSelectedVersion, getVersionCollection, - (selected, collection) => { - return selected ? collection[0].id === selected.id : null; - } + getSelectedIsCurrentFn ); // Version Comparison @@ -156,6 +187,43 @@ export const getComparisonConfigurations = createSelector( export const getComparisonConfigurationCount = createSelector(getComparisonConfigurations, (config) => config ? config.dates.length : 0); +export const getViewChangedOnly = createSelector(getCompareState, fromCompare.getViewChangedOnly); + +export const getLimitedPropertiesFn = (properties: SectionProperty[]) => { + return ([ + ...properties + .filter(p => p.differences) + .map(p => { + const parsed = { ...p }; + if (p.widget && p.widget.data) { + parsed.widget = { + ...p.widget, + data: p.widget.data.filter(item => item.differences) + }; + } + if (p.properties) { + parsed.properties = getLimitedPropertiesFn(p.properties); + } + return parsed; + }) + ]); +}; + +export const getLimitedConfigurationsFn = (configurations, limited) => configurations ? ({ + ...configurations, + sections: limited ? configurations.sections : + configurations.sections.map(s => ({ + ...s, + properties: getLimitedPropertiesFn(s.properties), + })) +}) : configurations; + +export const getLimitedComparisonConfigurations = createSelector( + getComparisonConfigurations, + getViewChangedOnly, + getLimitedConfigurationsFn +); + // Version Restoration export const getRestoreState = createSelector(getState, getRestoreStateFn); diff --git a/ui/src/app/metadata/domain/model/property.ts b/ui/src/app/metadata/domain/model/property.ts index 4768de9e4..51a4bf06e 100644 --- a/ui/src/app/metadata/domain/model/property.ts +++ b/ui/src/app/metadata/domain/model/property.ts @@ -5,10 +5,12 @@ export interface Property { value: any[]; items: Property; properties: Property[]; + differences?: boolean; widget?: { id: string; data?: {key: string, label: string}[]; dataUrl?: string; + differences?: string; [propertyName: string]: any; }; } diff --git a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html index fcbd80ab8..f1e04538a 100644 --- a/ui/src/app/schema-form/widget/filter-target/filter-target.component.html +++ b/ui/src/app/schema-form/widget/filter-target/filter-target.component.html @@ -78,7 +78,7 @@ Required for Regex   - + , {{ error.message }} diff --git a/ui/src/app/shared/validation/regex.validator.spec.ts b/ui/src/app/shared/validation/regex.validator.spec.ts index ce8af17ee..4835efd94 100644 --- a/ui/src/app/shared/validation/regex.validator.spec.ts +++ b/ui/src/app/shared/validation/regex.validator.spec.ts @@ -11,8 +11,8 @@ describe('RegexValidator', () => { expect(RegexValidator.isValidRegex(')')).toBe(false); }); - it('should return false if the regex doesnt begin and end with slashes', () => { - expect(RegexValidator.isValidRegex('abc')).toBe(false); + it('should return true even if the regex doesnt begin and end with slashes', () => { + expect(RegexValidator.isValidRegex('abc')).toBe(true); }); }); }); diff --git a/ui/src/app/shared/validation/regex.validator.ts b/ui/src/app/shared/validation/regex.validator.ts index be567a0f4..f05fadf8d 100644 --- a/ui/src/app/shared/validation/regex.validator.ts +++ b/ui/src/app/shared/validation/regex.validator.ts @@ -1,9 +1,7 @@ -const regexChecker = new RegExp('^\/|\/$', 'g'); - export class RegexValidator { static isValidRegex(pattern: string): boolean { if (!pattern) { - return true; + return false; } let regex; try { @@ -11,7 +9,7 @@ export class RegexValidator { } catch (err) { return false; } - return regexChecker.test(pattern); + return true; } } diff --git a/ui/src/testing/mockMetadataWizard.ts b/ui/src/testing/mockMetadataWizard.ts new file mode 100644 index 000000000..6c0135e29 --- /dev/null +++ b/ui/src/testing/mockMetadataWizard.ts @@ -0,0 +1,78 @@ +import { Wizard } from '../app/wizard/model/wizard'; + +export interface MockMetadata { + name: string; + serviceEnabled: boolean; + foo: { + bar: string; + baz: string; + }; +} + +export const MockMetadataWizard: Wizard = { + label: 'Metadata Source', + type: '@MetadataProvider', + validatorParams: [], + bindings: {}, + parser(changes: Partial, schema?: any): any { + return changes; + }, + formatter(changes: Partial, schema?: any): any { + return changes; + }, + getValidators(): { [key: string]: any } { + return {}; + }, + schema: '/api/ui/MetadataSources', + steps: [ + { + index: 1, + id: 'common', + label: 'label.sp-org-info', + fields: [ + 'name', + 'serviceEnabled' + ], + fieldsets: [ + { + type: 'group', + fields: [ + 'serviceProviderName', + 'entityId', + 'serviceEnabled', + 'organization' + ] + }, + { + type: 'group', + fields: [ + 'contacts' + ] + } + ] + }, + { + index: 2, + id: 'next', + label: 'something', + fields: [ + 'foo', + 'list' + ], + fieldsets: [ + { + type: 'group', + fields: [ + 'foo' + ] + }, + { + type: 'group', + fields: [ + 'list' + ] + } + ] + } + ] +}; diff --git a/ui/src/testing/sample-fbhttp-provider.json b/ui/src/testing/sample-fbhttp-provider.json new file mode 100644 index 000000000..c5d344120 --- /dev/null +++ b/ui/src/testing/sample-fbhttp-provider.json @@ -0,0 +1,94 @@ +{ + "createdDate": "2019-09-12T10:40:41.941", + "modifiedDate": "2019-09-12T10:41:31.787", + "createdBy": "root", + "modifiedBy": "root", + "current": true, + "name": "Update Provider Name", + "resourceId": "e34f39a7-9934-43bf-80c1-3a4e76e6dee8", + "xmlId": "updateid", + "enabled": true, + "requireValidMetadata": true, + "failFastInitialization": true, + "useDefaultPredicateRegistry": true, + "satisfyAnyPredicates": false, + "doInitialization": true, + "metadataFilters": [ + { + "createdDate": "2019-09-12T10:40:41.945", + "modifiedDate": "2019-09-12T10:40:41.945", + "createdBy": "root", + "modifiedBy": "root", + "current": false, + "resourceId": "315bca8c-cc61-4425-af1a-72e0b913102f", + "filterEnabled": false, + "requireSignedRoot": false, + "audId": 10, + "@type": "SignatureValidation", + "version": -230919157 + }, + { + "createdDate": "2019-09-12T10:40:41.946", + "modifiedDate": "2019-09-12T10:40:41.946", + "createdBy": "root", + "modifiedBy": "root", + "current": false, + "resourceId": "0d46d4a9-b65e-440e-99c4-07203fe50921", + "filterEnabled": false, + "removeRolelessEntityDescriptors": true, + "removeEmptyEntitiesDescriptors": true, + "retainedRoles": [], + "audId": 11, + "@type": "EntityRoleWhiteList", + "version": 198629661 + }, + { + "createdDate": "2019-09-12T10:42:38.904", + "modifiedDate": "2019-09-12T10:42:38.904", + "createdBy": "root", + "modifiedBy": "root", + "current": false, + "resourceId": "860bf7f3-3b3c-4dc7-b19b-111c9043d446", + "filterEnabled": false, + "requireSignedRoot": false, + "audId": 15, + "@type": "SignatureValidation", + "version": -747367480 + }, + { + "createdDate": "2019-09-12T10:42:38.905", + "modifiedDate": "2019-09-12T10:42:38.905", + "createdBy": "root", + "modifiedBy": "root", + "current": false, + "resourceId": "7f24ff1d-cb52-422f-8594-682dec40b3b7", + "filterEnabled": false, + "removeRolelessEntityDescriptors": true, + "removeEmptyEntitiesDescriptors": true, + "retainedRoles": [ + "md:SPSSODescriptor" + ], + "audId": 16, + "@type": "EntityRoleWhiteList", + "version": -280104078 + } + ], + "metadataURL": "https://idp.unicon.net/idp/shibboleth", + "backingFile": "test", + "initializeFromBackupFile": false, + "backupFileInitNextRefreshDelay": "PT1H", + "reloadableMetadataResolverAttributes": { + "minRefreshDelay": "PT30S", + "maxRefreshDelay": "PT10M", + "refreshDelayFactor": 0.5 + }, + "httpMetadataResolverAttributes": { + "connectionRequestTimeout": "PT10M", + "connectionTimeout": "PT30M", + "socketTimeout": "PT12H", + "disregardTLSCertificate": false + }, + "audId": 9, + "@type": "FileBackedHttpMetadataResolver", + "version": -1622542052 +} \ No newline at end of file