From 32596854ea810466ee6311a9dfe4af88e0b91016 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 6 Sep 2019 10:59:39 -0700 Subject: [PATCH 01/10] SHIBUI-1407 comparison highlights --- .../metadata-configuration.component.ts | 2 +- .../property/array-property.component.html | 16 ++++++++-------- .../property/primitive-property.component.html | 4 +++- .../app/metadata/configuration/reducer/index.ts | 6 ++++++ ui/src/app/metadata/domain/model/property.ts | 1 + 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts index 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 53682ea90..1a1fe28d1 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,13 +1,13 @@ -
+
-
{{ property.name }}
-
+
{{ property.name }}
+
-
+
{{ property.items.properties[prop].title }}
-
- {{ attr.label }} + {{ attr.label }}
true @@ -51,8 +51,8 @@
-
- {{ property.name }} +
+ {{ property.name }}

-

    +
    { + if (!array) { + return false; + } + return JSON.stringify(model[prop.id]) !== JSON.stringify(array[0][prop.id]); + }), value: models.map(model => { return model[prop.id]; }) diff --git a/ui/src/app/metadata/domain/model/property.ts b/ui/src/app/metadata/domain/model/property.ts index 4768de9e4..dba90e82e 100644 --- a/ui/src/app/metadata/domain/model/property.ts +++ b/ui/src/app/metadata/domain/model/property.ts @@ -5,6 +5,7 @@ export interface Property { value: any[]; items: Property; properties: Property[]; + differences?: boolean; widget?: { id: string; data?: {key: string, label: string}[]; From 530abb4ac8465d7fe94f14de06600f131b7ccd2f Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Fri, 6 Sep 2019 10:59:46 -0700 Subject: [PATCH 02/10] SHIBUI-1407 comparison highlights --- .../metadata-configuration.component.scss | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ui/src/app/metadata/configuration/component/metadata-configuration.component.scss 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..24b159af0 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss @@ -0,0 +1,19 @@ +@import '../../../../theme/_palette'; + +:host /deep/ { + .bg-diff { + background: #FEE8E9; + position: relative; + &::before { + $size: 24px; + content: "\f06a"; + font-family: 'FontAwesome'; + text-align: center; + width: $size; + height: $size; + position: absolute; + left: -$size; + top: 0.5rem; + } + } +} From f36f573954f41723b1ae3393e64efd25aa3e9148 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 10 Sep 2019 08:23:28 -0700 Subject: [PATCH 03/10] SHIBUI-1407 Implemented differences highlighting --- .../metadata-configuration.component.scss | 1 + .../property/array-property.component.html | 94 ++++++++++--------- .../property/array-property.component.ts | 7 ++ 3 files changed, 56 insertions(+), 46 deletions(-) diff --git a/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss b/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss index 24b159af0..82e7a5f37 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.scss @@ -4,6 +4,7 @@ .bg-diff { background: #FEE8E9; position: relative; + border-color: #CCC !important; &::before { $size: 24px; content: "\f06a"; 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 1a1fe28d1..28777731d 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,57 +1,59 @@ -
    - -
    {{ property.name }}
    -
    -
    -
    - {{ property.items.properties[prop].title }} -
    - -
    - {{ version[i][prop] }} -
    -
    - - -
    -
    +
    +
    {{ property.name }}
    +
    +
    +
    + {{ property.items.properties[prop].title }}
    + +
    + {{ version[i][prop] }} +
    +
    + - +
    +
    +
    +
    + + + - - - - - + + + + +
    - - -
    - -
    -
    -
    - {{ attr.label }} -
    - - true - - - false - -
    +
    +
    +
    + {{ attr.label }} +
    + + true + + + false +
    - +
    -
    +
    -
    +
    {{ property.name }}

    -

    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..f166aa155 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,6 +33,13 @@ export class ArrayPropertyComponent extends ConfigurationPropertyComponent imple return UriValidator.isUri(str); } + isDifferent(key: string): boolean { + const model = this.property.value || []; + return model + .map((value) => value ? value.indexOf(key) > -1 : false) + .reduce((current, val) => current !== val ? true : false, false); + } + get attributeList$(): Observable<{ key: string, label: string }[]> { if (this.property.widget && this.property.widget.hasOwnProperty('data')) { return of(this.property.widget.data); From ce2b6480fb9651d2793971656ad550e61144a4d1 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 10 Sep 2019 09:54:07 -0700 Subject: [PATCH 04/10] SHIBUI-1407 Added unit tests --- .../component/property/array-property.component.html | 2 +- .../property/array-property.component.spec.ts | 11 +++++++++++ .../component/property/array-property.component.ts | 3 +-- 3 files changed, 13 insertions(+), 3 deletions(-) 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 28777731d..9a5d1648e 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 @@ -36,7 +36,7 @@
    + [ngClass]="{'bg-diff': isDifferent(attr.key, property.value || [])}"> {{ attr.label }}
    { expect(app).toBeTruthy(); })); + describe('isDifferent method', () => { + it('should return true if the value is different between any of the lists', () => { + expect(app.isDifferent('foo', [['foo', 'bar', 'baz'], ['bar', 'baz']])).toBe(true); + expect(app.isDifferent('bar', [['bar'], null])).toBe(true); + }); + + it('should return false if the list of values is the same', () => { + expect(app.isDifferent('foo', [['foo', 'baz'], ['foo', 'bar']])).toBe(false); + }); + }); + describe('attributeList$ getter', () => { it('should return an empty list when no data or dataUrl is set', () => { app.attributeList$.subscribe((list) => { 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 f166aa155..7a042fa46 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,8 +33,7 @@ export class ArrayPropertyComponent extends ConfigurationPropertyComponent imple return UriValidator.isUri(str); } - isDifferent(key: string): boolean { - const model = this.property.value || []; + isDifferent(key: string, model: any): boolean { return model .map((value) => value ? value.indexOf(key) > -1 : false) .reduce((current, val) => current !== val ? true : false, false); From 0d0e9ed82a06a50647e24153f570896cdf7f505c Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Tue, 10 Sep 2019 14:57:32 -0700 Subject: [PATCH 05/10] SHIBUI-1408 added toggle button --- .../src/main/resources/i18n/messages.properties | 1 + .../configuration/action/compare.action.ts | 11 +++++++++-- .../container/metadata-comparison.component.html | 14 ++++++++++++-- .../container/metadata-comparison.component.ts | 16 +++++++++++++--- .../configuration/reducer/compare.reducer.ts | 10 +++++++++- .../app/metadata/configuration/reducer/index.ts | 2 ++ 6 files changed, 46 insertions(+), 8 deletions(-) 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/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..18ba345bb 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 } from 'rxjs'; import { Store } from '@ngrx/store'; import { ActivatedRoute } from '@angular/router'; -import { map } from 'rxjs/operators'; +import { map, withLatestFrom } from 'rxjs/operators'; import { ConfigurationState, getComparisonConfigurations, getComparisonConfigurationCount } from '../reducer'; -import { CompareVersionRequest, ClearVersions } from '../action/compare.action'; +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, @@ -34,9 +38,15 @@ export class MetadataComparisonComponent implements OnDestroy { this.versions$ = this.store.select(getComparisonConfigurations); this.numVersions$ = this.store.select(getComparisonConfigurationCount); this.type$ = this.store.select(fromReducer.getConfigurationModelType); + + this.sub = this.limiter.pipe( + withLatestFrom(this.limited$), + map(([compare, limit]) => new ViewChanged(!limit)) + ).subscribe(this.store); } ngOnDestroy(): void { + this.sub.unsubscribe(); this.store.dispatch(new ClearVersions()); } } 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.ts b/ui/src/app/metadata/configuration/reducer/index.ts index c53dc7538..807c1ab17 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -162,6 +162,8 @@ export const getComparisonConfigurations = createSelector( export const getComparisonConfigurationCount = createSelector(getComparisonConfigurations, (config) => config ? config.dates.length : 0); +export const getViewChangedOnly = createSelector(getCompareState, fromCompare.getViewChangedOnly); + // Version Restoration export const getRestoreState = createSelector(getState, getRestoreStateFn); From 246fa6363e015ccab589cbeb66b8eb028dec7525 Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Wed, 11 Sep 2019 18:18:19 -0700 Subject: [PATCH 06/10] SHIBUI-1461 Removed some unnecessary scrollTo's. --- backend/src/integration/resources/SHIBUI-1333.side | 9 +-------- .../src/integration/resources/SHIBUI-1334-1.side | 14 -------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/backend/src/integration/resources/SHIBUI-1333.side b/backend/src/integration/resources/SHIBUI-1333.side index 9b66bde58..5e086eddb 100644 --- a/backend/src/integration/resources/SHIBUI-1333.side +++ b/backend/src/integration/resources/SHIBUI-1333.side @@ -1891,13 +1891,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": "", @@ -1967,4 +1960,4 @@ }], "urls": ["http://localhost:10101/"], "plugins": [] -} +} \ No newline at end of file diff --git a/backend/src/integration/resources/SHIBUI-1334-1.side b/backend/src/integration/resources/SHIBUI-1334-1.side index 0b2bcdfbe..bffdda8f1 100644 --- a/backend/src/integration/resources/SHIBUI-1334-1.side +++ b/backend/src/integration/resources/SHIBUI-1334-1.side @@ -1891,13 +1891,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": "", @@ -2131,13 +2124,6 @@ ["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": "", From 9d2b7c1d9f3fed1333b1269d9b714502cf8b01af Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 12 Sep 2019 12:50:42 -0700 Subject: [PATCH 07/10] SHIBUI-1408 Added toggle for displaying only changes in comparison --- .../metadata-configuration.component.html | 34 +++-- .../property/array-property.component.html | 26 +--- .../property/array-property.component.spec.ts | 39 +---- .../property/array-property.component.ts | 16 +-- .../metadata-comparison.component.ts | 8 +- .../metadata/configuration/model/section.ts | 4 + .../configuration/reducer/index.spec.ts | 136 +++++++++++++++++- .../metadata/configuration/reducer/index.ts | 82 +++++++++-- ui/src/testing/mockMetadataWizard.ts | 78 ++++++++++ ui/src/testing/sample-fbhttp-provider.json | 94 ++++++++++++ 10 files changed, 414 insertions(+), 103 deletions(-) create mode 100644 ui/src/testing/mockMetadataWizard.ts create mode 100644 ui/src/testing/sample-fbhttp-provider.json 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/property/array-property.component.html b/ui/src/app/metadata/configuration/component/property/array-property.component.html index 380e0105b..ed7591702 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 @@ -20,20 +20,6 @@
    - -
    - {{ version[i][prop] }} -
    -
    - - -
    -
    @@ -47,18 +33,18 @@
    -
    -
    +
    - {{ attr.label }} + [ngClass]="{'bg-diff': property.differences && item.differences}"> + {{ item.label }}
    - + true - + false
    diff --git a/ui/src/app/metadata/configuration/component/property/array-property.component.spec.ts b/ui/src/app/metadata/configuration/component/property/array-property.component.spec.ts index 6260a4e50..363b3163a 100644 --- a/ui/src/app/metadata/configuration/component/property/array-property.component.spec.ts +++ b/ui/src/app/metadata/configuration/component/property/array-property.component.spec.ts @@ -67,22 +67,9 @@ describe('Array Property Component', () => { expect(app).toBeTruthy(); })); - describe('isDifferent method', () => { - it('should return true if the value is different between any of the lists', () => { - expect(app.isDifferent('foo', [['foo', 'bar', 'baz'], ['bar', 'baz']])).toBe(true); - expect(app.isDifferent('bar', [['bar'], null])).toBe(true); - }); - - it('should return false if the list of values is the same', () => { - expect(app.isDifferent('foo', [['foo', 'baz'], ['foo', 'bar']])).toBe(false); - }); - }); - 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 = [ @@ -98,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 7a042fa46..e3e995c2b 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,20 +33,8 @@ export class ArrayPropertyComponent extends ConfigurationPropertyComponent imple return UriValidator.isUri(str); } - isDifferent(key: string, model: any): boolean { - return model - .map((value) => value ? value.indexOf(key) > -1 : false) - .reduce((current, val) => current !== val ? true : false, false); - } - - 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 }[] { + return this.property.widget.data; } } 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 18ba345bb..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,9 +1,9 @@ import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; -import { Observable, BehaviorSubject, Subscription } from 'rxjs'; +import { Observable, BehaviorSubject, Subscription, combineLatest } from 'rxjs'; import { Store } from '@ngrx/store'; import { ActivatedRoute } from '@angular/router'; import { map, withLatestFrom } from 'rxjs/operators'; -import { ConfigurationState, getComparisonConfigurations, getComparisonConfigurationCount } from '../reducer'; +import { ConfigurationState, getComparisonConfigurationCount } from '../reducer'; import { CompareVersionRequest, ClearVersions, ViewChanged } from '../action/compare.action'; import { MetadataConfiguration } from '../model/metadata-configuration'; import * as fromReducer from '../reducer'; @@ -35,10 +35,12 @@ 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)) 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/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 807c1ab17..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,15 +90,11 @@ export const assignValueToProperties = (models, properties, definition: any): an default: return { ...prop, - differences: models.some((model, index, array) => { - if (!array) { - return false; - } - return JSON.stringify(model[prop.id]) !== JSON.stringify(array[0][prop.id]); - }), + differences, value: models.map(model => { return model[prop.id]; - }) + }), + widget }; } }); @@ -109,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) + })) }); }; @@ -133,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 @@ -164,6 +189,41 @@ export const getComparisonConfigurationCount = createSelector(getComparisonConfi export const getViewChangedOnly = createSelector(getCompareState, fromCompare.getViewChangedOnly); +export const getLimitedPropertiesFn = (properties: SectionProperty[]) => { + return ([ + ...properties + .filter(p => p.differences) + .map(p => { + const parsed = { ...p }; + if (p.widget && p.widget.data) { + parsed.widget = { + ...p.widget, + data: p.widget.data.filter(item => item.differences) + }; + } + if (p.properties) { + parsed.properties = getLimitedPropertiesFn(p.properties); + } + return parsed; + }) + ]); +}; + +export const getLimitedConfigurationsFn = (configurations, limited) => configurations ? ({ + ...configurations, + sections: limited ? configurations.sections : + 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/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 From 4a1a258a7e50352f84e0f45e663614db02295baa Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 12 Sep 2019 13:57:35 -0700 Subject: [PATCH 08/10] SHIBUI-1391 Updated regex test --- .../widget/filter-target/filter-target.component.html | 2 +- ui/src/app/shared/validation/regex.validator.spec.ts | 4 ++-- ui/src/app/shared/validation/regex.validator.ts | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) 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; } } From ec47f91dc797bb1132ff82ef90a00715859b4935 Mon Sep 17 00:00:00 2001 From: Ryan Mathis Date: Thu, 12 Sep 2019 15:02:24 -0700 Subject: [PATCH 09/10] SHIBUI-1408 Added a11y element for changes --- .../component/property/array-property.component.html | 10 +++++++--- .../component/property/array-property.component.ts | 2 +- .../property/primitive-property.component.html | 3 +++ ui/src/app/metadata/domain/model/property.ts | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) 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 ed7591702..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,7 +1,8 @@
    {{ property.name }}
    -
    +
    + Changed:
    {{ property.items.properties[prop].title }}
    @@ -36,7 +37,9 @@
    + [ngClass]="{'bg-diff': property.differences && item.differences}" + tabindex="0"> + Changed: {{ item.label }}
    -
    +
    + Changed: {{ property.name }}

    -

    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 e3e995c2b..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,7 +33,7 @@ export class ArrayPropertyComponent extends ConfigurationPropertyComponent imple return UriValidator.isUri(str); } - get dataList(): { key: string, label: string }[] { + 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 6abfa315d..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,3 +1,5 @@ +
    +Changed:
    @@ -9,4 +11,5 @@ class="d-block" role="definition" [ngStyle]="{'width': width}">{{ v ? v : (v === false) ? v : '-' }} +
    \ No newline at end of file diff --git a/ui/src/app/metadata/domain/model/property.ts b/ui/src/app/metadata/domain/model/property.ts index dba90e82e..51a4bf06e 100644 --- a/ui/src/app/metadata/domain/model/property.ts +++ b/ui/src/app/metadata/domain/model/property.ts @@ -10,6 +10,7 @@ export interface Property { id: string; data?: {key: string, label: string}[]; dataUrl?: string; + differences?: string; [propertyName: string]: any; }; } From aed3385a7da8ff93337e4c6a1e64586e6f53363e Mon Sep 17 00:00:00 2001 From: Bill Smith Date: Fri, 13 Sep 2019 09:43:19 -0700 Subject: [PATCH 10/10] SHIBUI-1408-a11y Added a fix for changes to the UI. --- backend/src/integration/resources/SHIBUI-1334-1.side | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/integration/resources/SHIBUI-1334-1.side b/backend/src/integration/resources/SHIBUI-1334-1.side index bffdda8f1..6487ddc87 100644 --- a/backend/src/integration/resources/SHIBUI-1334-1.side +++ b/backend/src/integration/resources/SHIBUI-1334-1.side @@ -2128,9 +2128,9 @@ "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" @@ -2138,10 +2138,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" }, {