diff --git a/backend/src/main/resources/entity-attributes-filters-ui-schema.json b/backend/src/main/resources/entity-attributes-filters-ui-schema.json index d67364b21..c7bb95b1a 100644 --- a/backend/src/main/resources/entity-attributes-filters-ui-schema.json +++ b/backend/src/main/resources/entity-attributes-filters-ui-schema.json @@ -46,7 +46,7 @@ }, "properties": { "entityAttributesFilterTargetType": { - "title": "", + "title": "label.filter-target-type", "type": "string", "default": "ENTITY", "oneOf": [ @@ -71,6 +71,7 @@ ] }, "value": { + "title": "label.filter-target-value", "type": "array", "buttons": [ { diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index f3054d636..90edf9bc7 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -41,6 +41,7 @@ action.copy=Copy action.choose-file=Choose File action.search-by=Search By action.preview=Preview +action.preview-xml=Preview XML action.select-metadata-filter-type=Select a metadata filter type action.add-authentication-method=Add Authentication Method action.move-up=Move Up @@ -51,6 +52,9 @@ action.manage-filters=Manage Filters action.version-history=Version History action.options=Options action.xml=XML +action.manage=Manage +action.close=Close +action.back-to-top=Back to Top value.enabled=Enabled value.disabled=Disabled @@ -239,7 +243,11 @@ label.filter-name=Filter Name label.filter-enabled=Filter Enabled label.filter-target=FilterTarget label.filter-type=Filter Type +label.filter-target-type=Filter Target Type +label.filter-target-value=Filter Target Value +label.target=Filter Target label.option=Option +label.options=Options label.value=Value label.binding-type=Binding Type label.sign-assertion=Sign Assertions @@ -261,6 +269,8 @@ label.make-default=Make Default label.metadata-provider-name-dashboard-display-only=Metadata Provider Name (Dashboard Display Only) label.default-authentication-methods=Default Authentication Method(s) label.new-of-type=New { type } +label.filters=Filters +label.attributes=Attributes label.metadata-filter-name=Metadata Filter Name (Dashboard Display Only) label.filter-enable=Enable this Filter? @@ -458,6 +468,9 @@ message.required-for-regex=Required for Regex message.file-doesnt-exist=The requested file to be processed does not exist on the server. message.database-constraint=There was a database constraint problem processing the request. Check the request to ensure that fields that must be unique are truly unique. +message.no-filters=No Filters +message.no-filters-added=No filters have been added to this Metadata Provider + tooltip.entity-id=Entity ID tooltip.service-provider-name=Service Provider Name (Dashboard Display Only) tooltip.force-authn=Disallows use (or reuse) of authentication results and login flows that don\u0027t provide a real-time proof of user presence in the login process diff --git a/ui/package-lock.json b/ui/package-lock.json index 25fad932c..02851a581 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -3511,7 +3511,6 @@ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -6468,8 +6467,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -6490,14 +6488,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6512,20 +6508,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6642,8 +6635,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6655,7 +6647,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6670,7 +6661,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6678,14 +6668,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6704,7 +6692,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6785,8 +6772,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6798,7 +6784,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6884,8 +6869,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6921,7 +6905,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6941,7 +6924,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6985,14 +6967,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -9302,8 +9282,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true, - "optional": true + "dev": true }, "http-signature": { "version": "1.2.0", @@ -9545,8 +9524,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true, - "optional": true + "dev": true }, "loose-envify": { "version": "1.3.1", diff --git a/ui/src/app/app.routing.ts b/ui/src/app/app.routing.ts index 99b8d88de..34ad45010 100644 --- a/ui/src/app/app.routing.ts +++ b/ui/src/app/app.routing.ts @@ -15,7 +15,8 @@ const routes: Routes = [ @NgModule({ imports: [RouterModule.forRoot(routes, { - preloadingStrategy: PreloadAllModules + preloadingStrategy: PreloadAllModules, + scrollOffset: [0, 64] })], exports: [RouterModule] }) diff --git a/ui/src/app/core/reducer/configuration.reducer.spec.ts b/ui/src/app/core/reducer/configuration.reducer.spec.ts new file mode 100644 index 000000000..e8e8a802e --- /dev/null +++ b/ui/src/app/core/reducer/configuration.reducer.spec.ts @@ -0,0 +1,35 @@ +import { reducer } from './configuration.reducer'; +import * as fromConfiguration from './configuration.reducer'; +import * as actions from '../action/configuration.action'; + +describe('Configuration Reducer', () => { + const initialState: fromConfiguration.ConfigState = { + roles: [] + }; + + describe('undefined action', () => { + it('should return the default state', () => { + const result = reducer(undefined, {} as any); + expect(result).toEqual(initialState); + }); + }); + + describe('Role Load Request', () => { + it('should set fetching to true', () => { + const action = new actions.LoadRoleSuccess(['ADMIN']); + const result = reducer(initialState, action); + expect(result).toEqual( + { + ...initialState, + roles: ['ADMIN'] + } + ); + }); + }); + + describe('selector functions', () => { + it('should return the roles from state', () => { + expect(fromConfiguration.getRoles(initialState)).toEqual([]); + }); + }); +}); diff --git a/ui/src/app/core/reducer/index.spec.ts b/ui/src/app/core/reducer/index.spec.ts index f14a08006..5a1fbef3e 100644 --- a/ui/src/app/core/reducer/index.spec.ts +++ b/ui/src/app/core/reducer/index.spec.ts @@ -10,7 +10,6 @@ describe('Core index reducers', () => { version: fromVersion.initialState as fromVersion.VersionState, config: fromConfig.initialState as fromConfig.ConfigState }; - describe('getUserStateFn function', () => { it('should return the user state', () => { expect(fromIndex.getUserStateFn(state)).toEqual(state.user); @@ -21,4 +20,21 @@ describe('Core index reducers', () => { expect(fromIndex.getVersionStateFn(state)).toEqual(state.version); }); }); + describe('getConfigStateFn function', () => { + it('should return the config state', () => { + expect(fromIndex.getConfigStateFn(state)).toEqual(state.config); + }); + }); + describe('filterRolesFn', () => { + it('should return the roles that are not `non roles`', () => { + expect(fromIndex.filterRolesFn(['ROLE_ADMIN', 'ROLE_NONE'])).toEqual(['ROLE_ADMIN']); + }); + }); + describe('isUserAdminFn', () => { + it('should check if the provided user has the ROLE_ADMIN role', () => { + expect(fromIndex.isUserAdminFn({role: 'ROLE_ADMIN'})).toBe(true); + expect(fromIndex.isUserAdminFn({role: 'ROLE_USER'})).toBe(false); + expect(fromIndex.isUserAdminFn(null)).toBe(false); + }); + }); }); diff --git a/ui/src/app/core/reducer/index.ts b/ui/src/app/core/reducer/index.ts index f2d64e934..f5e5b94c2 100644 --- a/ui/src/app/core/reducer/index.ts +++ b/ui/src/app/core/reducer/index.ts @@ -40,7 +40,7 @@ export const getVersionLoading = createSelector(getVersionState, fromVersion.get export const getVersionError = createSelector(getVersionState, fromVersion.getVersionError); export const filterRolesFn = (roles: string[]) => roles.filter(r => r !== 'ROLE_NONE'); -export const isUserAdminFn = (user) => user ? user.role === 'ROLE_ADMIN' : null; +export const isUserAdminFn = (user) => user ? user.role === 'ROLE_ADMIN' : false; export const getConfigState = createSelector(getCoreFeature, getConfigStateFn); export const getRoles = createSelector(getConfigState, fromConfig.getRoles); diff --git a/ui/src/app/metadata/configuration/action/configuration.action.ts b/ui/src/app/metadata/configuration/action/configuration.action.ts index b5b86ade1..78e834eea 100644 --- a/ui/src/app/metadata/configuration/action/configuration.action.ts +++ b/ui/src/app/metadata/configuration/action/configuration.action.ts @@ -4,10 +4,6 @@ import { Schema } from '../model/schema'; import { Wizard } from '../../../wizard/model'; export enum ConfigurationActionTypes { - LOAD_METADATA_REQUEST = '[Metadata Configuration] Load Metadata Request', - LOAD_METADATA_SUCCESS = '[Metadata Configuration] Load Metadata Success', - LOAD_METADATA_ERROR = '[Metadata Configuration] Load Metadata Error', - LOAD_SCHEMA_REQUEST = '[Metadata Configuration] Load Schema Request', LOAD_SCHEMA_SUCCESS = '[Metadata Configuration] Load Schema Success', LOAD_SCHEMA_ERROR = '[Metadata Configuration] Load Schema Error', @@ -26,24 +22,6 @@ export enum ConfigurationActionTypes { CLEAR = '[Metadata Configuration] Clear' } -export class LoadMetadataRequest implements Action { - readonly type = ConfigurationActionTypes.LOAD_METADATA_REQUEST; - - constructor(public payload: { id: string, type: string }) { } -} - -export class LoadMetadataSuccess implements Action { - readonly type = ConfigurationActionTypes.LOAD_METADATA_SUCCESS; - - constructor(public payload: Metadata) { } -} - -export class LoadMetadataError implements Action { - readonly type = ConfigurationActionTypes.LOAD_METADATA_ERROR; - - constructor(public payload: any) { } -} - export class LoadSchemaRequest implements Action { readonly type = ConfigurationActionTypes.LOAD_SCHEMA_REQUEST; @@ -83,7 +61,7 @@ export class LoadXmlError implements Action { export class SetMetadata implements Action { readonly type = ConfigurationActionTypes.SET_METADATA; - constructor(public payload: Metadata) { } + constructor(public payload: { id: string, type: string }) { } } export class SetDefinition implements Action { @@ -113,9 +91,6 @@ export class ClearConfiguration implements Action { } export type ConfigurationActionsUnion = - | LoadMetadataRequest - | LoadMetadataSuccess - | LoadMetadataError | LoadSchemaRequest | LoadSchemaSuccess | LoadSchemaError diff --git a/ui/src/app/metadata/configuration/component/array-property.component.html b/ui/src/app/metadata/configuration/component/array-property.component.html index 969bd175e..d51a8e0fb 100644 --- a/ui/src/app/metadata/configuration/component/array-property.component.html +++ b/ui/src/app/metadata/configuration/component/array-property.component.html @@ -1,7 +1,7 @@
-
{{ property.name }}
-
+
{{ property.name }}
+
{{ property.items.properties[prop].title }} @@ -11,18 +11,19 @@ [ngbPopover]="version[i][prop]" triggers="mouseenter:mouseleave" container="body" - placement="left" - [ngStyle]="{'width': width}"> + [ngStyle]="{'width': width}" + class="text-truncate" + popoverClass="popover-lg popover-info"> {{ version[i][prop] }}
- — + -
- + @@ -30,32 +31,52 @@ -
- {{ attr.label }} -
- - true - - - false - +
+ +
+
+
+ {{ attr.label }} +
+ + true + + + false + +
-
- {{ property.name }} +
+ {{ property.name }} -

-
    -
  • +

    -

    +
      +
    • + +   + {{ item }}
    + +
      +
    • + {{ item }} +
    • +
    +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/array-property.component.scss b/ui/src/app/metadata/configuration/component/array-property.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/ui/src/app/metadata/configuration/component/array-property.component.ts b/ui/src/app/metadata/configuration/component/array-property.component.ts index 35d50dedd..a17ad01da 100644 --- a/ui/src/app/metadata/configuration/component/array-property.component.ts +++ b/ui/src/app/metadata/configuration/component/array-property.component.ts @@ -1,18 +1,21 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, Input, OnChanges, Output, EventEmitter } from '@angular/core'; import { Property } from '../../domain/model/property'; import { Observable, of } from 'rxjs'; import { AttributesService } from '../../domain/service/attributes.service'; import { ConfigurationPropertyComponent } from './configuration-property.component'; +import UriValidator from '../../../shared/validation/uri.validator'; @Component({ selector: 'array-property', templateUrl: './array-property.component.html', - styleUrls: [] + styleUrls: ['./array-property.component.scss'] }) export class ArrayPropertyComponent extends ConfigurationPropertyComponent implements OnChanges { @Input() property: Property; + @Output() preview: EventEmitter = new EventEmitter(); + range = []; constructor( @@ -26,6 +29,10 @@ export class ArrayPropertyComponent extends ConfigurationPropertyComponent imple this.range = [...Array(keys).keys()]; } + isUrl(str: string): boolean { + 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); diff --git a/ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts b/ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts index 5ef0452e0..aa7fd9478 100644 --- a/ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts +++ b/ui/src/app/metadata/configuration/component/configuration-property.component.spec.ts @@ -62,7 +62,8 @@ describe('Configuration Property Component', () => { describe('getItemType method', () => { it('should return the item`s type', () => { - expect(app.getItemType({ widget: { id: 'string' } } as Property)).toBe('string'); + expect(app.getItemType({items: { widget: { id: 'string' } } } as Property)).toBe('string'); + expect(app.getItemType({items: {}} as Property)).toBe('default'); expect(app.getItemType({} as Property)).toBe('default'); }); }); diff --git a/ui/src/app/metadata/configuration/component/configuration-property.component.ts b/ui/src/app/metadata/configuration/component/configuration-property.component.ts index 001a8235b..5418bde30 100644 --- a/ui/src/app/metadata/configuration/component/configuration-property.component.ts +++ b/ui/src/app/metadata/configuration/component/configuration-property.component.ts @@ -16,8 +16,10 @@ export class ConfigurationPropertyComponent { return Object.keys(schema.properties); } - getItemType(items: Property): string { - return items.widget ? items.widget.id : 'default'; + getItemType(property: Property): string { + const items = property.items; + const def = 'default'; + return items ? items.widget ? items.widget.id : def : def; } get width(): string { diff --git a/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.html b/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.html new file mode 100644 index 000000000..b8df62a11 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.html @@ -0,0 +1,47 @@ +
+ {{ index + 1 }} +
+ + +
+ + {{ filter['@type'] }} + + Enabled + Disabled + +
+
+
+
+
+ +   + Edit + + +
+
+ + +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.ts b/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.ts new file mode 100644 index 000000000..ede42a55a --- /dev/null +++ b/ui/src/app/metadata/configuration/component/filter-configuration-list-item.component.ts @@ -0,0 +1,52 @@ +import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { MetadataFilter } from '../../domain/model'; +import { MetadataConfigurationService } from '../service/configuration.service'; +import { MetadataConfiguration } from '../model/metadata-configuration'; +import { PreviewEntity } from '../../domain/action/entity.action'; +import { Metadata } from '../../domain/domain.type'; +import * as fromRoot from '../../../app.reducer'; + +@Component({ + selector: 'filter-configuration-list-item', + templateUrl: './filter-configuration-list-item.component.html' +}) +export class FilterConfigurationListItemComponent implements OnChanges { + @Input() filter: MetadataFilter; + @Input() index: number; + @Input() isFirst: boolean; + @Input() isLast: boolean; + + @Output() onUpdateOrderUp: EventEmitter = new EventEmitter(); + @Output() onUpdateOrderDown: EventEmitter = new EventEmitter(); + @Output() onRemove: EventEmitter = new EventEmitter(); + + open = false; + configuration: MetadataConfiguration; + definition: any; + + constructor( + private configService: MetadataConfigurationService, + private store: Store + ) {} + + onPreview($event: { data: any, parent: Metadata }): void { + this.store.dispatch(new PreviewEntity({ + id: $event.data, + entity: this.definition.getEntity($event.parent) + })); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.filter) { + this.definition = this.configService.getDefinition(this.filter['@type']); + this.configService.loadSchema(this.definition.schema).subscribe(schema => { + this.configuration = this.configService.getMetadataConfiguration( + this.filter, + this.definition, + schema + ); + }); + } + } +} diff --git a/ui/src/app/metadata/configuration/component/filter-configuration-list.component.html b/ui/src/app/metadata/configuration/component/filter-configuration-list.component.html new file mode 100644 index 000000000..c49a7738c --- /dev/null +++ b/ui/src/app/metadata/configuration/component/filter-configuration-list.component.html @@ -0,0 +1,17 @@ +
    +
  • + +
  • +
+
+

No Filters

+

No filters have been added to this Metadata Provider

+
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/filter-configuration-list.component.ts b/ui/src/app/metadata/configuration/component/filter-configuration-list.component.ts new file mode 100644 index 000000000..5208a7f4c --- /dev/null +++ b/ui/src/app/metadata/configuration/component/filter-configuration-list.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; +import { FilterListComponent } from '../../filter/component/filter-list.component'; + +@Component({ + selector: 'filter-configuration-list', + templateUrl: './filter-configuration-list.component.html' +}) +export class FilterConfigurationListComponent extends FilterListComponent { } diff --git a/ui/src/app/metadata/configuration/component/filter-target-property.component.html b/ui/src/app/metadata/configuration/component/filter-target-property.component.html new file mode 100644 index 000000000..f8bfbb5c8 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/filter-target-property.component.html @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/filter-target-property.component.spec.ts b/ui/src/app/metadata/configuration/component/filter-target-property.component.spec.ts new file mode 100644 index 000000000..98c08d4c4 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/filter-target-property.component.spec.ts @@ -0,0 +1,66 @@ +import { Component, ViewChild } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; +import { Property } from '../../domain/model/property'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { getStepProperty } from '../../domain/utility/configuration'; +import { FilterTargetPropertyComponent } from './filter-target-property.component'; +import { PrimitivePropertyComponent } from './primitive-property.component'; +import { ArrayPropertyComponentStub, PrimitivePropertyComponentStub } from '../../../../testing/property-component.stub'; +import { AttributesService } from '../../domain/service/attributes.service'; +import { of } from 'rxjs'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(FilterTargetPropertyComponent) + public componentUnderTest: FilterTargetPropertyComponent; + + property: Property = getStepProperty(SCHEMA.properties.formatFilterTarget, { + formatFilterTargetType: 'ENTITY_ID', + value: [ + 'foo', + 'bar', + 'baz' + ] + }, SCHEMA.definitions); +} + +describe('Filter Target Property Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: FilterTargetPropertyComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbPopoverModule, + MockI18nModule, + RouterTestingModule + ], + declarations: [ + FilterTargetPropertyComponent, + PrimitivePropertyComponentStub, + ArrayPropertyComponentStub, + TestHostComponent + ], + providers: [] + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should accept a property input', async(() => { + expect(app).toBeTruthy(); + })); +}); diff --git a/ui/src/app/metadata/configuration/component/filter-target-property.component.ts b/ui/src/app/metadata/configuration/component/filter-target-property.component.ts new file mode 100644 index 000000000..6b7ebf216 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/filter-target-property.component.ts @@ -0,0 +1,26 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Property } from '../../domain/model/property'; +import { ConfigurationPropertyComponent } from './configuration-property.component'; + +@Component({ + selector: 'filter-target-property', + templateUrl: './filter-target-property.component.html', + styleUrls: [] +}) +export class FilterTargetPropertyComponent extends ConfigurationPropertyComponent { + @Input() parent: Property; + + @Output() preview: EventEmitter = new EventEmitter(); + + constructor() { + super(); + } + + onPreview(data: any) { + this.preview.emit({ + parent: this.parent, + data + }); + } +} + diff --git a/ui/src/app/metadata/configuration/component/history-list.component.html b/ui/src/app/metadata/configuration/component/history-list.component.html index 2983c151c..82a275060 100644 --- a/ui/src/app/metadata/configuration/component/history-list.component.html +++ b/ui/src/app/metadata/configuration/component/history-list.component.html @@ -21,9 +21,9 @@
- Current (v{{ i + 1 }}) - v{{ i + 1 }} - {{ version.date | date }} + Current (v{{ history.length - i }}) + v{{ history.length - (i) }} + {{ version.date | date:'medium' }} {{ version.creator }}
-
- Option - - Value - {{ date | date:'medium' }} - +
+
+ Option + + Value + {{ date | date:'medium' }} + +
+ +
- -
\ No newline at end of file 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 fd90fb58b..86e8bd23b 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts @@ -1,8 +1,7 @@ -import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { MetadataConfiguration } from '../model/metadata-configuration'; -import { Property } from '../../domain/model/property'; -import { Observable, of } from 'rxjs'; +import { Metadata } from '../../domain/domain.type'; @Component({ selector: 'metadata-configuration', @@ -12,16 +11,26 @@ import { Observable, of } from 'rxjs'; }) export class MetadataConfigurationComponent { @Input() configuration: MetadataConfiguration; + @Input() definition: any; + @Input() entity: Metadata; + @Input() numbered = true; + @Input() editable = true; + + @Output() preview: EventEmitter = new EventEmitter(); constructor( private router: Router, private activatedRoute: ActivatedRoute - ) { } + ) {} edit(id: string): void { this.router.navigate(['../', 'edit', id], { relativeTo: this.activatedRoute.parent }); } + onPreview($event): void { + this.preview.emit($event); + } + get width(): string { const columns = this.configuration.dates.length; return `${Math.floor(100 / (columns + 1)) }%`; diff --git a/ui/src/app/metadata/configuration/component/object-property.component.html b/ui/src/app/metadata/configuration/component/object-property.component.html index 0c19ac9d0..e31da24ad 100644 --- a/ui/src/app/metadata/configuration/component/object-property.component.html +++ b/ui/src/app/metadata/configuration/component/object-property.component.html @@ -2,6 +2,16 @@ - + + + + + diff --git a/ui/src/app/metadata/configuration/component/object-property.component.spec.ts b/ui/src/app/metadata/configuration/component/object-property.component.spec.ts index 6299b20d5..8817711c9 100644 --- a/ui/src/app/metadata/configuration/component/object-property.component.spec.ts +++ b/ui/src/app/metadata/configuration/component/object-property.component.spec.ts @@ -1,15 +1,16 @@ -import { Component, ViewChild, Input } from '@angular/core'; +import { Component, ViewChild } from '@angular/core'; import { TestBed, async, ComponentFixture } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { NgbDropdownModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; import { Property } from '../../domain/model/property'; import { MockI18nModule } from '../../../../testing/i18n.stub'; import { ObjectPropertyComponent } from './object-property.component'; import { SCHEMA } from '../../../../testing/form-schema.stub'; -import { getStepProperties, getStepProperty } from '../../domain/utility/configuration'; +import { getStepProperty } from '../../domain/utility/configuration'; import { PrimitivePropertyComponent } from './primitive-property.component'; import { ArrayPropertyComponent } from './array-property.component'; +import { FilterTargetPropertyComponent } from './filter-target-property.component'; @Component({ template: ` @@ -44,6 +45,7 @@ describe('Object Property Component', () => { ObjectPropertyComponent, PrimitivePropertyComponent, ArrayPropertyComponent, + FilterTargetPropertyComponent, TestHostComponent ] }).compileComponents(); diff --git a/ui/src/app/metadata/configuration/component/object-property.component.ts b/ui/src/app/metadata/configuration/component/object-property.component.ts index f7892877e..1fafafa25 100644 --- a/ui/src/app/metadata/configuration/component/object-property.component.ts +++ b/ui/src/app/metadata/configuration/component/object-property.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, Output, EventEmitter } from '@angular/core'; import { Property } from '../../domain/model/property'; import { ConfigurationPropertyComponent } from './configuration-property.component'; @@ -10,6 +10,7 @@ import { ConfigurationPropertyComponent } from './configuration-property.compone export class ObjectPropertyComponent extends ConfigurationPropertyComponent { @Input() property: Property; + @Output() preview: EventEmitter = new EventEmitter(); constructor() { super(); diff --git a/ui/src/app/metadata/configuration/component/primitive-property.component.html b/ui/src/app/metadata/configuration/component/primitive-property.component.html index 608f4acdd..4d93c07cc 100644 --- a/ui/src/app/metadata/configuration/component/primitive-property.component.html +++ b/ui/src/app/metadata/configuration/component/primitive-property.component.html @@ -1,4 +1,4 @@ -
+
-

Source Configuration

+

+ Source + Provider + Configuration +

diff --git a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts index bc82b8a14..71bb05ab0 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts @@ -7,6 +7,8 @@ import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; import { MetadataConfiguration } from '../model/metadata-configuration'; import { ConfigurationComponent } from './configuration.component'; import * as fromConfiguration from '../reducer'; +import * as fromProviders from '../../provider/reducer'; +import * as fromResolvers from '../../resolver/reducer'; import { MockI18nModule } from '../../../../testing/i18n.stub'; @Component({ @@ -36,6 +38,8 @@ describe('Metadata Configuration Page Component', () => { NgbDropdownModule, StoreModule.forRoot({ 'metadata-configuration': combineReducers(fromConfiguration.reducers), + 'provider': combineReducers(fromProviders.reducers), + 'resolver': combineReducers(fromResolvers.reducers) }), MockI18nModule, RouterTestingModule diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts index 17ce4b99c..4c06d7f9d 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.ts @@ -1,14 +1,15 @@ -import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { takeUntil, map, withLatestFrom, filter } from 'rxjs/operators'; +import { Component, ChangeDetectionStrategy, OnDestroy, HostListener } from '@angular/core'; +import { ActivatedRoute, Router, Scroll, Event } from '@angular/router'; +import { takeUntil, map, withLatestFrom, filter, timeout, delay } from 'rxjs/operators'; import { Store } from '@ngrx/store'; -import { Observable, Subject } from 'rxjs'; +import { Observable, Subject, interval } from 'rxjs'; import * as fromConfiguration from '../reducer'; -import { LoadMetadataRequest, ClearConfiguration } from '../action/configuration.action'; +import { ClearConfiguration, SetMetadata } from '../action/configuration.action'; import { LoadHistoryRequest, ClearHistory, SelectVersion } from '../action/history.action'; import * as fromReducer from '../reducer'; +import { ViewportScroller } from '@angular/common'; @Component({ selector: 'configuration-page', @@ -20,6 +21,7 @@ export class ConfigurationComponent implements OnDestroy { private ngUnsubscribe: Subject = new Subject(); name$: Observable; + type$: Observable; constructor( private store: Store, @@ -27,7 +29,7 @@ export class ConfigurationComponent implements OnDestroy { ) { this.routerState.params.pipe( takeUntil(this.ngUnsubscribe), - map(params => new LoadMetadataRequest({id: params.id, type: params.type})) + map(params => new SetMetadata({id: params.id, type: params.type})) ).subscribe(store); this.routerState.params.pipe( @@ -53,6 +55,7 @@ export class ConfigurationComponent implements OnDestroy { }); this.name$ = this.store.select(fromReducer.getConfigurationModelName); + this.type$ = this.store.select(fromReducer.getConfigurationModelType); } ngOnDestroy() { 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 fabdb5338..d5b533716 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.html @@ -1,10 +1,7 @@ -
- +
  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 701c99d2a..92019297d 100644 --- a/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-comparison.component.ts @@ -3,7 +3,7 @@ import { Observable } from 'rxjs'; import { Store } from '@ngrx/store'; import { ActivatedRoute } from '@angular/router'; import { map } from 'rxjs/operators'; -import { ConfigurationState, getVersionConfigurations } from '../reducer'; +import { ConfigurationState, getVersionConfigurations, getVersionConfigurationCount } from '../reducer'; import { Metadata } from '../../domain/domain.type'; import { CompareVersionRequest } from '../action/compare.action'; import { MetadataConfiguration } from '../model/metadata-configuration'; @@ -17,6 +17,7 @@ import { MetadataConfiguration } from '../model/metadata-configuration'; export class MetadataComparisonComponent { versions$: Observable; + numVersions$: Observable; constructor( private store: Store, @@ -28,5 +29,6 @@ export class MetadataComparisonComponent { ).subscribe(this.store); this.versions$ = this.store.select(getVersionConfigurations); + this.numVersions$ = this.store.select(getVersionConfigurationCount); } } diff --git a/ui/src/app/metadata/configuration/container/metadata-history.component.spec.ts b/ui/src/app/metadata/configuration/container/metadata-history.component.spec.ts index 43d8cc11b..4a322ee3c 100644 --- a/ui/src/app/metadata/configuration/container/metadata-history.component.spec.ts +++ b/ui/src/app/metadata/configuration/container/metadata-history.component.spec.ts @@ -117,10 +117,10 @@ describe('Metadata Version History Component', () => { ]; const sorted = instance.sortVersionsByDate(versions); - expect(sorted[0].id).toEqual('bar'); - expect(sorted[1].id).toEqual('baz'); - expect(sorted[2].id).toEqual('foo'); - expect(sorted[3].id).toEqual('baz2'); + expect(sorted[0].id).toEqual('baz2'); + expect(sorted[1].id).toEqual('foo'); + expect(sorted[2].id).toEqual('baz'); + expect(sorted[3].id).toEqual('bar'); }); }); }); diff --git a/ui/src/app/metadata/configuration/container/metadata-history.component.ts b/ui/src/app/metadata/configuration/container/metadata-history.component.ts index 6d62e6ed7..c49e0c70f 100644 --- a/ui/src/app/metadata/configuration/container/metadata-history.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-history.component.ts @@ -5,6 +5,7 @@ import { ConfigurationState, getVersionCollection } from '../reducer'; import { MetadataVersion } from '../model/version'; import { CompareVersionRequest } from '../action/compare.action'; import { Router, ActivatedRoute } from '@angular/router'; +import { map } from 'rxjs/operators'; @Component({ selector: 'metadata-history', @@ -21,7 +22,8 @@ export class MetadataHistoryComponent { private router: Router, private route: ActivatedRoute ) { - this.history$ = this.store.select(getVersionCollection); + this.history$ = this.store.select(getVersionCollection) + .pipe(map(versions => this.sortVersionsByDate(versions))); } sortVersionsByDate(versions: MetadataVersion[]): MetadataVersion[] { @@ -29,7 +31,7 @@ export class MetadataHistoryComponent { const aDate = new Date(a.date).getTime(); const bDate = new Date(b.date).getTime(); return aDate === bDate ? 0 : aDate < bDate ? -1 : 1; - }); + }).reverse(); } compareVersions(versions: MetadataVersion[]): void { diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.html b/ui/src/app/metadata/configuration/container/metadata-options.component.html index 9c4dbb489..d99166c11 100644 --- a/ui/src/app/metadata/configuration/container/metadata-options.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.html @@ -1,18 +1,58 @@ -
+