diff --git a/backend/src/main/resources/i18n/messages.properties b/backend/src/main/resources/i18n/messages.properties index 89c5cd00b..389162b05 100644 --- a/backend/src/main/resources/i18n/messages.properties +++ b/backend/src/main/resources/i18n/messages.properties @@ -48,9 +48,13 @@ action.move-down=Move Down action.edit=Edit action.add-filter=Add Filter action.manage-filters=Manage Filters +action.version-history=Version History +action.options=Options +action.xml=XML value.enabled=Enabled value.disabled=Disabled +value.current=Current value.none=None value.file=File value.memory=Memory @@ -402,6 +406,9 @@ label.current=Current label.restore=Restore label.compare-selected=Compare Selected +label.saved=Saved +label.by=by + message.delete-user-title=Delete User? message.delete-user-body=You are requesting to delete a user. If you complete this process the user will be removed. This cannot be undone. Do you wish to continue? diff --git a/ui/src/app/metadata/configuration/action/configuration.action.ts b/ui/src/app/metadata/configuration/action/configuration.action.ts index 1aa442601..b5b86ade1 100644 --- a/ui/src/app/metadata/configuration/action/configuration.action.ts +++ b/ui/src/app/metadata/configuration/action/configuration.action.ts @@ -65,7 +65,7 @@ export class LoadSchemaError implements Action { export class LoadXmlRequest implements Action { readonly type = ConfigurationActionTypes.LOAD_XML_REQUEST; - constructor(public payload: string) { } + constructor(public payload: { id: string, type: string }) { } } export class LoadXmlSuccess implements Action { diff --git a/ui/src/app/metadata/configuration/action/history.action.ts b/ui/src/app/metadata/configuration/action/history.action.ts new file mode 100644 index 000000000..5ee05ea65 --- /dev/null +++ b/ui/src/app/metadata/configuration/action/history.action.ts @@ -0,0 +1,52 @@ +import { Action } from '@ngrx/store'; +import { MetadataHistory } from '../model/history'; + +export enum HistoryActionTypes { + LOAD_HISTORY_REQUEST = '[Configuration History] Load History Request', + LOAD_HISTORY_SUCCESS = '[Configuration History] Load History Success', + LOAD_HISTORY_ERROR = '[Configuration History] Load History Error', + SET_HISTORY = '[Configuration History] Set History', + CLEAR_HISTORY = '[Configuration History] Clear History', + SELECT_VERSION = '[Configuration History] Select Version' +} + +export class LoadHistoryRequest implements Action { + readonly type = HistoryActionTypes.LOAD_HISTORY_REQUEST; + + constructor(public payload: { id: string, type: string }) { } +} + +export class LoadHistorySuccess implements Action { + readonly type = HistoryActionTypes.LOAD_HISTORY_SUCCESS; + + constructor(public payload: MetadataHistory) { } +} + +export class LoadHistoryError implements Action { + readonly type = HistoryActionTypes.LOAD_HISTORY_ERROR; + + constructor(public payload: any) { } +} + +export class SelectVersion implements Action { + readonly type = HistoryActionTypes.SELECT_VERSION; + constructor(public payload: string | null) { } +} + +export class SetHistory implements Action { + readonly type = HistoryActionTypes.SET_HISTORY; + + constructor(public payload: MetadataHistory) {} +} + +export class ClearHistory implements Action { + readonly type = HistoryActionTypes.CLEAR_HISTORY; +} + +export type HistoryActionsUnion = + | LoadHistoryRequest + | LoadHistorySuccess + | LoadHistoryError + | SetHistory + | ClearHistory + | SelectVersion; diff --git a/ui/src/app/metadata/version/component/history-list.component.html b/ui/src/app/metadata/configuration/component/history-list.component.html similarity index 74% rename from ui/src/app/metadata/version/component/history-list.component.html rename to ui/src/app/metadata/configuration/component/history-list.component.html index 6fa941e6e..2983c151c 100644 --- a/ui/src/app/metadata/version/component/history-list.component.html +++ b/ui/src/app/metadata/configuration/component/history-list.component.html @@ -12,7 +12,7 @@ - +
@@ -21,19 +21,19 @@
- Current (v{{ version.versionNumber }}) - v{{ version.versionNumber }} - {{ version.saveDate | date }} - {{ version.changedBy }} + Current (v{{ i + 1 }}) + v{{ i + 1 }} + {{ version.date | date }} + {{ version.creator }} - - \ No newline at end of file diff --git a/ui/src/app/metadata/version/component/history-list.component.spec.ts b/ui/src/app/metadata/configuration/component/history-list.component.spec.ts similarity index 78% rename from ui/src/app/metadata/version/component/history-list.component.spec.ts rename to ui/src/app/metadata/configuration/component/history-list.component.spec.ts index 4cb6770eb..43d49a29f 100644 --- a/ui/src/app/metadata/version/component/history-list.component.spec.ts +++ b/ui/src/app/metadata/configuration/component/history-list.component.spec.ts @@ -8,16 +8,15 @@ import { MetadataVersion } from '../model/version'; export const TestData = { versions: [ { - versionNumber: 1, - saveDate: new Date(), - changedBy: 'admin', - actions: [] + id: 'foo', + date: new Date().toDateString(), + creator: 'admin' } ] }; @Component({ - template: `` + template: `` }) class TestHostComponent { @ViewChild(HistoryListComponent) @@ -32,7 +31,7 @@ class TestHostComponent { describe('Metadata History List Component', () => { let fixture: ComponentFixture; let instance: TestHostComponent; - let table: HistoryListComponent; + let list: HistoryListComponent; beforeEach(() => { TestBed.configureTestingModule({ @@ -48,32 +47,34 @@ describe('Metadata History List Component', () => { fixture = TestBed.createComponent(TestHostComponent); instance = fixture.componentInstance; - table = instance.componentUnderTest; + list = instance.componentUnderTest; fixture.detectChanges(); }); it('should compile', () => { - expect(table).toBeDefined(); + expect(list).toBeDefined(); }); describe('compare selected', () => { it('should allow the user to toggle selected versions for comparison', () => { - table.toggleVersionSelected(TestData.versions[0]); - expect(table.selected.length).toBe(1); + list.toggleVersionSelected(TestData.versions[0]); + expect(list.selected.length).toBe(1); }); it('should emit an event with the selected values when the Compare Selected button is clicked', () => { spyOn(instance, 'compare'); const selected = TestData.versions; - table.compareSelected(selected); + list.compareSelected(selected); fixture.detectChanges(); expect(instance.compare).toHaveBeenCalledWith(selected); }); + }); + describe('restore', () => { it('should emit an event with the selected version when the Restore button is clicked', () => { spyOn(instance, 'restore'); const selected = TestData.versions[0]; - table.restoreVersion(selected); + list.restoreVersion(selected); fixture.detectChanges(); expect(instance.restore).toHaveBeenCalledWith(selected); }); diff --git a/ui/src/app/metadata/version/component/history-list.component.ts b/ui/src/app/metadata/configuration/component/history-list.component.ts similarity index 96% rename from ui/src/app/metadata/version/component/history-list.component.ts rename to ui/src/app/metadata/configuration/component/history-list.component.ts index 34696a98e..7e03468e2 100644 --- a/ui/src/app/metadata/version/component/history-list.component.ts +++ b/ui/src/app/metadata/configuration/component/history-list.component.ts @@ -9,7 +9,7 @@ import { MetadataVersion } from '../model/version'; styleUrls: [] }) export class HistoryListComponent { - @Input() history: MetadataHistory; + @Input() history: MetadataVersion[]; @Output() compare: EventEmitter = new EventEmitter(); @Output() restore: EventEmitter = new EventEmitter(); 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 258b9aa77..4f58d07f1 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html @@ -1,5 +1,5 @@
-
+

@@ -7,10 +7,10 @@

{{ section.label | translate }}

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 732b920d3..9e94bcdbd 100644 --- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts +++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.ts @@ -1,4 +1,5 @@ import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; import { MetadataConfiguration } from '../model/metadata-configuration'; @Component({ @@ -10,5 +11,12 @@ import { MetadataConfiguration } from '../model/metadata-configuration'; export class MetadataConfigurationComponent { @Input() configuration: MetadataConfiguration; - constructor() { } + constructor( + private router: Router, + private activatedRoute: ActivatedRoute + ) { } + + edit(id: string): void { + this.router.navigate(['../', 'edit', id], { relativeTo: this.activatedRoute.parent }); + } } diff --git a/ui/src/app/metadata/configuration/component/metadata-header.component.html b/ui/src/app/metadata/configuration/component/metadata-header.component.html new file mode 100644 index 000000000..19f183ba8 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/metadata-header.component.html @@ -0,0 +1,19 @@ +
+
+

+ Version {{ versionNumber }} +

+

+ Saved  + {{ version.date | date }}, + by  + {{ version.creator }} +

+

+ Enabled +   + Disabled + Current +

+
+
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/component/metadata-header.component.spec.ts b/ui/src/app/metadata/configuration/component/metadata-header.component.spec.ts new file mode 100644 index 000000000..e8db42c36 --- /dev/null +++ b/ui/src/app/metadata/configuration/component/metadata-header.component.spec.ts @@ -0,0 +1,58 @@ +import { Component, ViewChild } from '@angular/core'; +import { TestBed, async, ComponentFixture } from '@angular/core/testing'; +import { MockI18nModule } from '../../../../testing/i18n.stub'; +import { MetadataVersion } from '../model/version'; +import { MetadataHeaderComponent } from './metadata-header.component'; + +@Component({ + template: ` + + ` +}) +class TestHostComponent { + @ViewChild(MetadataHeaderComponent) + public componentUnderTest: MetadataHeaderComponent; + + isEnabled = true; + + version: MetadataVersion = { + id: 'foo', + creator: 'foobar', + date: new Date().toDateString() + }; + versionNumber = 1; + isCurrent = false; +} + +describe('Metadata Header Component', () => { + + let fixture: ComponentFixture; + let instance: TestHostComponent; + let app: MetadataHeaderComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + MockI18nModule + ], + declarations: [ + MetadataHeaderComponent, + TestHostComponent + ] + }).compileComponents(); + + fixture = TestBed.createComponent(TestHostComponent); + instance = fixture.componentInstance; + app = instance.componentUnderTest; + fixture.detectChanges(); + })); + + it('should accept a property input', async(() => { + expect(app).toBeTruthy(); + })); +}); diff --git a/ui/src/app/metadata/configuration/component/metadata-header.component.ts b/ui/src/app/metadata/configuration/component/metadata-header.component.ts new file mode 100644 index 000000000..8d0dcc3db --- /dev/null +++ b/ui/src/app/metadata/configuration/component/metadata-header.component.ts @@ -0,0 +1,19 @@ +import { Component, Input } from '@angular/core'; +import { Metadata, MetadataTypes } from '../../domain/domain.type'; +import { MetadataVersion } from '../model/version'; + +@Component({ + selector: 'metadata-header', + templateUrl: './metadata-header.component.html', + styleUrls: [] +}) + +export class MetadataHeaderComponent { + @Input() isEnabled: boolean; + @Input() version: MetadataVersion; + @Input() versionNumber: number; + @Input() isCurrent: boolean; + + constructor() {} +} + diff --git a/ui/src/app/metadata/configuration/configuration.module.ts b/ui/src/app/metadata/configuration/configuration.module.ts index 2cf56580d..9aa2cf880 100644 --- a/ui/src/app/metadata/configuration/configuration.module.ts +++ b/ui/src/app/metadata/configuration/configuration.module.ts @@ -12,13 +12,17 @@ import { MetadataConfigurationService } from './service/configuration.service'; import * as fromConfig from './reducer'; import { MetadataConfigurationEffects } from './effect/configuration.effect'; import { ConfigurationPropertyComponent } from './component/configuration-property.component'; -import { DomainModule } from '../domain/domain.module'; import { PrimitivePropertyComponent } from './component/primitive-property.component'; import { ObjectPropertyComponent } from './component/object-property.component'; import { ArrayPropertyComponent } from './component/array-property.component'; import { RouterModule } from '@angular/router'; import { MetadataOptionsComponent } from './container/metadata-options.component'; import { MetadataXmlComponent } from './container/metadata-xml.component'; +import { MetadataHeaderComponent } from './component/metadata-header.component'; +import { MetadataHistoryEffects } from './effect/history.effect'; +import { MetadataHistoryService } from './service/history.service'; +import { MetadataHistoryComponent } from './container/metadata-history.component'; +import { HistoryListComponent } from './component/history-list.component'; @NgModule({ declarations: [ @@ -29,7 +33,10 @@ import { MetadataXmlComponent } from './container/metadata-xml.component'; PrimitivePropertyComponent, ObjectPropertyComponent, ArrayPropertyComponent, - ConfigurationComponent + ConfigurationComponent, + MetadataHeaderComponent, + MetadataHistoryComponent, + HistoryListComponent ], entryComponents: [], imports: [ @@ -46,7 +53,8 @@ export class MetadataConfigurationModule { return { ngModule: RootMetadataConfigurationModule, providers: [ - MetadataConfigurationService + MetadataConfigurationService, + MetadataHistoryService ] }; } @@ -56,7 +64,7 @@ export class MetadataConfigurationModule { imports: [ MetadataConfigurationModule, StoreModule.forFeature('metadata-configuration', fromConfig.reducers), - EffectsModule.forFeature([MetadataConfigurationEffects]) + EffectsModule.forFeature([MetadataConfigurationEffects, MetadataHistoryEffects]) ], providers: [] }) diff --git a/ui/src/app/metadata/configuration/configuration.routing.ts b/ui/src/app/metadata/configuration/configuration.routing.ts index 8e7327792..d27b29872 100644 --- a/ui/src/app/metadata/configuration/configuration.routing.ts +++ b/ui/src/app/metadata/configuration/configuration.routing.ts @@ -2,6 +2,7 @@ import { Routes } from '@angular/router'; import { ConfigurationComponent } from './container/configuration.component'; import { MetadataOptionsComponent } from './container/metadata-options.component'; import { MetadataXmlComponent } from './container/metadata-xml.component'; +import { MetadataHistoryComponent } from './container/metadata-history.component'; export const ConfigurationRoutes: Routes = [ { @@ -19,6 +20,10 @@ export const ConfigurationRoutes: Routes = [ { path: 'xml', component: MetadataXmlComponent + }, + { + path: 'history', + component: MetadataHistoryComponent } ] } diff --git a/ui/src/app/metadata/configuration/configuration.values.ts b/ui/src/app/metadata/configuration/configuration.values.ts new file mode 100644 index 000000000..c7f696eae --- /dev/null +++ b/ui/src/app/metadata/configuration/configuration.values.ts @@ -0,0 +1,10 @@ +import { MetadataSourceEditor } from '../domain/model/wizards/metadata-source-editor'; + +export enum PATHS { + resolver = 'EntityDescriptor', + provider = 'MetadataResolvers' +} + +export const DEFINITIONS = { + resolver: MetadataSourceEditor +}; diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts index d18e5ab34..2c49c0961 100644 --- a/ui/src/app/metadata/configuration/container/configuration.component.ts +++ b/ui/src/app/metadata/configuration/container/configuration.component.ts @@ -5,8 +5,10 @@ import { ActivatedRoute, Params } from '@angular/router'; import * as fromConfiguration from '../reducer'; import { MetadataConfiguration } from '../model/metadata-configuration'; -import { takeUntil, map } from 'rxjs/operators'; -import { LoadMetadataRequest, ClearConfiguration } from '../action/configuration.action'; +import { takeUntil, map, withLatestFrom, filter } from 'rxjs/operators'; +import { LoadMetadataRequest, ClearConfiguration, LoadXmlRequest } from '../action/configuration.action'; +import { LoadHistoryRequest, ClearHistory, SelectVersion } from '../action/history.action'; +import * as fromReducer from '../reducer'; @Component({ selector: 'configuration-page', @@ -25,11 +27,34 @@ export class ConfigurationComponent implements OnDestroy { takeUntil(this.ngUnsubscribe), map(params => new LoadMetadataRequest({id: params.id, type: params.type})) ).subscribe(store); + + this.routerState.params.pipe( + takeUntil(this.ngUnsubscribe), + map(params => new LoadHistoryRequest({ id: params.id, type: params.type })) + ).subscribe(store); + + this.store.select(fromReducer.getVersionCollection).pipe( + takeUntil(this.ngUnsubscribe), + withLatestFrom( + this.routerState.queryParams + ), + map(([collection, params]) => { + if (collection && collection.length) { + return params.version || collection[0].id; + } + return null; + }) + ).subscribe(version => { + if (version) { + this.store.dispatch(new SelectVersion(version)); + } + }); } ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); this.store.dispatch(new ClearConfiguration()); + this.store.dispatch(new ClearHistory()); } } diff --git a/ui/src/app/metadata/configuration/container/metadata-history.component.html b/ui/src/app/metadata/configuration/container/metadata-history.component.html new file mode 100644 index 000000000..225912756 --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-history.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/ui/src/app/metadata/version/container/version-history.component.spec.ts b/ui/src/app/metadata/configuration/container/metadata-history.component.spec.ts similarity index 65% rename from ui/src/app/metadata/version/container/version-history.component.spec.ts rename to ui/src/app/metadata/configuration/container/metadata-history.component.spec.ts index 4398b5128..084d79ef0 100644 --- a/ui/src/app/metadata/version/container/version-history.component.spec.ts +++ b/ui/src/app/metadata/configuration/container/metadata-history.component.spec.ts @@ -1,11 +1,13 @@ import { Component, ViewChild, Input, EventEmitter, Output } from '@angular/core'; import { TestBed, ComponentFixture } from '@angular/core/testing'; import { MockI18nModule } from '../../../../testing/i18n.stub'; -import { VersionHistoryComponent } from './version-history.component'; +import { MetadataHistoryComponent } from './metadata-history.component'; import { MetadataHistory } from '../model/history'; import { MetadataVersion } from '../model/version'; -import { HistoryService } from '../service/history.service'; +import { MetadataHistoryService } from '../service/history.service'; import { of } from 'rxjs'; +import { StoreModule, combineReducers } from '@ngrx/store'; +import * as fromConfiguration from '../reducer'; export const TestData = { versions: [ @@ -33,26 +35,29 @@ const MockHistoryService = { }; describe('Metadata Version History Component', () => { - let fixture: ComponentFixture; - let instance: VersionHistoryComponent; + let fixture: ComponentFixture; + let instance: MetadataHistoryComponent; beforeEach(() => { TestBed.configureTestingModule({ providers: [ { - provide: HistoryService, useValue: MockHistoryService + provide: MetadataHistoryService, useValue: MockHistoryService } ], imports: [ - MockI18nModule + MockI18nModule, + StoreModule.forRoot({ + 'metadata-configuration': combineReducers(fromConfiguration.reducers), + }), ], declarations: [ MockHistoryListComponent, - VersionHistoryComponent + MetadataHistoryComponent ], }); - fixture = TestBed.createComponent(VersionHistoryComponent); + fixture = TestBed.createComponent(MetadataHistoryComponent); instance = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/ui/src/app/metadata/configuration/container/metadata-history.component.ts b/ui/src/app/metadata/configuration/container/metadata-history.component.ts new file mode 100644 index 000000000..6fb60bdb9 --- /dev/null +++ b/ui/src/app/metadata/configuration/container/metadata-history.component.ts @@ -0,0 +1,22 @@ +import { Component, ChangeDetectionStrategy } from '@angular/core'; +import { Observable } from 'rxjs'; +import { Store } from '@ngrx/store'; +import { ConfigurationState, getVersionCollection } from '../reducer'; +import { MetadataVersion } from '../model/version'; + +@Component({ + selector: 'metadata-history', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './metadata-history.component.html', + styleUrls: [] +}) +export class MetadataHistoryComponent { + + history$: Observable; + + constructor( + private store: Store + ) { + this.history$ = this.store.select(getVersionCollection); + } +} 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 5e6b85384..9c4dbb489 100644 --- a/ui/src/app/metadata/configuration/container/metadata-options.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.html @@ -1,3 +1,18 @@ \ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts b/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts index 2e35d8c75..0cb1bd5b0 100644 --- a/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts @@ -8,6 +8,9 @@ import { MetadataConfiguration } from '../model/metadata-configuration'; import * as fromConfiguration from '../reducer'; import { MockI18nModule } from '../../../../testing/i18n.stub'; import { MetadataOptionsComponent } from './metadata-options.component'; +import { Metadata } from '../../domain/domain.type'; +import { MetadataVersion } from '../model/version'; +import { CommonModule } from '@angular/common'; @Component({ selector: 'metadata-configuration', @@ -17,6 +20,17 @@ class MetadataConfigurationComponent { @Input() configuration: MetadataConfiguration; } +@Component({ + selector: 'metadata-header', + template: `` +}) +class MetadataHeaderComponent { + @Input() isEnabled: boolean; + @Input() version: MetadataVersion; + @Input() versionNumber: number; + @Input() isCurrent: boolean; +} + @Component({ template: ` @@ -44,18 +58,20 @@ describe('Metadata Options Page Component', () => { 'metadata-configuration': combineReducers(fromConfiguration.reducers), }), MockI18nModule, - RouterTestingModule + RouterTestingModule, + CommonModule ], declarations: [ MetadataOptionsComponent, MetadataConfigurationComponent, + MetadataHeaderComponent, TestHostComponent ], }).compileComponents(); store = TestBed.get(Store); spyOn(store, 'dispatch'); - spyOn(store, 'select'); + spyOn(store, 'select').and.callThrough(); fixture = TestBed.createComponent(TestHostComponent); instance = fixture.componentInstance; diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.ts b/ui/src/app/metadata/configuration/container/metadata-options.component.ts index bc2d0bb0d..461252f1d 100644 --- a/ui/src/app/metadata/configuration/container/metadata-options.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-options.component.ts @@ -2,8 +2,17 @@ import { Store } from '@ngrx/store'; import { Component, ChangeDetectionStrategy } from '@angular/core'; import { Observable } from 'rxjs'; -import * as fromConfiguration from '../reducer'; +import { + ConfigurationState, + getConfigurationSections, + getConfigurationModel, + getSelectedVersion, + getSelectedVersionNumber, + getSelectedIsCurrent +} from '../reducer'; import { MetadataConfiguration } from '../model/metadata-configuration'; +import { MetadataVersion } from '../model/version'; +import { map } from 'rxjs/operators'; @Component({ selector: 'metadata-options-page', @@ -14,10 +23,20 @@ import { MetadataConfiguration } from '../model/metadata-configuration'; export class MetadataOptionsComponent { configuration$: Observable; + isEnabled$: Observable; + version$: Observable; + versionNumber$: Observable; + isCurrent$: Observable; constructor( - private store: Store + private store: Store ) { - this.configuration$ = this.store.select(fromConfiguration.getConfigurationSections); + this.configuration$ = this.store.select(getConfigurationSections); + this.isEnabled$ = this.store.select(getConfigurationModel).pipe( + map(config => config ? ('serviceEnabled' in config) ? config.serviceEnabled : config.enabled : false) + ); + this.version$ = this.store.select(getSelectedVersion); + this.versionNumber$ = this.store.select(getSelectedVersionNumber); + this.isCurrent$ = this.store.select(getSelectedIsCurrent); } } diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.html b/ui/src/app/metadata/configuration/container/metadata-xml.component.html index 57560ea78..613dc7699 100644 --- a/ui/src/app/metadata/configuration/container/metadata-xml.component.html +++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.html @@ -1,7 +1,15 @@
-
{{ xml$ | async }}
- +
+
+ Options + XML +
+
+
+
{{ xml$ | async }}
+ +
\ No newline at end of file diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.ts b/ui/src/app/metadata/configuration/container/metadata-xml.component.ts index 26b62323e..1a56906a0 100644 --- a/ui/src/app/metadata/configuration/container/metadata-xml.component.ts +++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.ts @@ -13,8 +13,6 @@ import { DownloadXml } from '../action/configuration.action'; }) export class MetadataXmlComponent { - private ngUnsubscribe: Subject = new Subject(); - entity: Metadata; entity$: Observable; xml: string; diff --git a/ui/src/app/metadata/configuration/effect/history.effect.ts b/ui/src/app/metadata/configuration/effect/history.effect.ts new file mode 100644 index 000000000..0bda812ec --- /dev/null +++ b/ui/src/app/metadata/configuration/effect/history.effect.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { Effect, Actions, ofType } from '@ngrx/effects'; +import { switchMap, catchError, map } from 'rxjs/operators'; +import { of } from 'rxjs'; +import { LoadHistoryRequest, HistoryActionTypes, LoadHistorySuccess, LoadHistoryError, SetHistory } from '../action/history.action'; +import { MetadataHistoryService } from '../service/history.service'; + +@Injectable() +export class MetadataHistoryEffects { + + @Effect() + loadHistory$ = this.actions$.pipe( + ofType(HistoryActionTypes.LOAD_HISTORY_REQUEST), + switchMap(action => + this.historyService + .query(action.payload.id, action.payload.type) + .pipe( + map(history => new LoadHistorySuccess(history)), + catchError(error => of(new LoadHistoryError(error))) + ) + ) + ); + + @Effect() + loadHistorySuccess$ = this.actions$.pipe( + ofType(HistoryActionTypes.LOAD_HISTORY_SUCCESS), + map(action => new SetHistory(action.payload)) + ); + + constructor( + private historyService: MetadataHistoryService, + private actions$: Actions + ) { } +} diff --git a/ui/src/app/metadata/version/model/history.ts b/ui/src/app/metadata/configuration/model/history.ts similarity index 100% rename from ui/src/app/metadata/version/model/history.ts rename to ui/src/app/metadata/configuration/model/history.ts diff --git a/ui/src/app/metadata/configuration/model/version.ts b/ui/src/app/metadata/configuration/model/version.ts new file mode 100644 index 000000000..0eb7cb815 --- /dev/null +++ b/ui/src/app/metadata/configuration/model/version.ts @@ -0,0 +1,5 @@ +export interface MetadataVersion { + id: string; + date: string; + creator: string; +} diff --git a/ui/src/app/metadata/configuration/reducer/history.reducer.spec.ts b/ui/src/app/metadata/configuration/reducer/history.reducer.spec.ts new file mode 100644 index 000000000..a3bc830e5 --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/history.reducer.spec.ts @@ -0,0 +1,70 @@ +import { reducer } from './history.reducer'; +import * as fromHistory from './history.reducer'; +import * as actions from '../action/history.action'; +import { MetadataHistory } from '../model/history'; + +describe('History Reducer', () => { + + const baseState = fromHistory.initialState; + + const history: MetadataHistory = { + versions: [ + { + id: '1', + date: new Date().toLocaleDateString(), + creator: 'foo' + }, + { + id: '2', + date: new Date().toDateString(), + creator: 'foo' + } + ] + }; + + describe('undefined action', () => { + it('should return the default state', () => { + const result = reducer(undefined, {} as any); + + expect(result).toEqual(fromHistory.initialState); + }); + }); + + describe('SET_HISTORY action', () => { + it('should set the state metadata model', () => { + const action = new actions.SetHistory(history); + const result = reducer(fromHistory.initialState, action); + + expect(Object.keys(result.entities)).toEqual(['1', '2']); + }); + }); + + describe('SELECT_VERSION action', () => { + it('should set the state metadata model', () => { + const action = new actions.SelectVersion('1'); + const result = reducer(baseState, action); + + expect(result).toEqual({ ...baseState, selectedVersionId: '1' }); + }); + }); + + describe('CLEAR action', () => { + it('should clear the state and reset to initial state', () => { + const action = new actions.ClearHistory(); + const result = reducer({ + ...baseState, + ...history + }, action); + + expect(result).toEqual(baseState); + }); + }); + + describe('selector functions', () => { + describe('getSelectedId', () => { + it('should return the selected version id', () => { + expect(fromHistory.getSelectedVersionId({ ...baseState, selectedVersionId: '1' })).toBe('1'); + }); + }); + }); +}); diff --git a/ui/src/app/metadata/configuration/reducer/history.reducer.ts b/ui/src/app/metadata/configuration/reducer/history.reducer.ts new file mode 100644 index 000000000..8a2566f59 --- /dev/null +++ b/ui/src/app/metadata/configuration/reducer/history.reducer.ts @@ -0,0 +1,50 @@ +import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; +import { HistoryActionTypes, HistoryActionsUnion } from '../action/history.action'; +import { MetadataVersion } from '../model/version'; + +export interface HistoryState extends EntityState { + selectedVersionId: string; +} + +export function sortByDate(a: MetadataVersion, b: MetadataVersion): number { + return a.date.localeCompare(b.date); +} + +export const adapter: EntityAdapter = createEntityAdapter({ + sortComparer: sortByDate, + selectId: (model: MetadataVersion) => model.id +}); + +export const initialState: HistoryState = adapter.getInitialState({ + selectedVersionId: null +}); + +export function reducer(state = initialState, action: HistoryActionsUnion): HistoryState { + switch (action.type) { + case HistoryActionTypes.SET_HISTORY: + return adapter.addAll(action.payload.versions, { + ...state, + selectedVersionId: state.selectedVersionId + }); + case HistoryActionTypes.SELECT_VERSION: + return { + ...state, + selectedVersionId: action.payload + }; + case HistoryActionTypes.CLEAR_HISTORY: + return adapter.removeAll({ + ...initialState + }); + default: { + return state; + } + } +} + +export const getSelectedVersionId = (state: HistoryState) => state.selectedVersionId; +export const { + selectIds: selectVersionIds, + selectEntities: selectVersionEntities, + selectAll: selectAllVersions, + selectTotal: selectVersionTotal +} = adapter.getSelectors(); diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts index d4c2fcc9c..702e731f4 100644 --- a/ui/src/app/metadata/configuration/reducer/index.ts +++ b/ui/src/app/metadata/configuration/reducer/index.ts @@ -1,19 +1,22 @@ import { createSelector, createFeatureSelector } from '@ngrx/store'; -import merge from 'deepmerge'; import * as fromRoot from '../../../app.reducer'; import * as fromConfiguration from './configuration.reducer'; +import * as fromHistory from './history.reducer'; import { WizardStep } from '../../../wizard/model'; import * as utils from '../../domain/utility/configuration'; import { getSplitSchema } from '../../../wizard/reducer'; +import { getInCollectionFn } from '../../domain/domain.util'; export interface ConfigurationState { configuration: fromConfiguration.State; + history: fromHistory.HistoryState; } export const reducers = { - configuration: fromConfiguration.reducer + configuration: fromConfiguration.reducer, + history: fromHistory.reducer }; export interface State extends fromRoot.State { @@ -23,6 +26,7 @@ export interface State extends fromRoot.State { export const getState = createFeatureSelector('metadata-configuration'); export const getConfigurationStateFn = (state: ConfigurationState) => state.configuration; +export const getHistoryStateFn = (state: ConfigurationState) => state.history; export const getConfigurationState = createSelector(getState, getConfigurationStateFn); export const getConfigurationModel = createSelector(getConfigurationState, fromConfiguration.getModel); @@ -56,3 +60,26 @@ export const getConfigurationSections = createSelector( getConfigurationSchema, getConfigurationSectionsFn ); + +// Version History + +export const getHistoryState = createSelector(getState, getHistoryStateFn); + +export const getVersionEntities = createSelector(getHistoryState, fromHistory.selectVersionEntities); +export const getSelectedVersionId = createSelector(getHistoryState, fromHistory.getSelectedVersionId); +export const getVersionIds = createSelector(getHistoryState, fromHistory.selectVersionIds); +export const getVersionCollection = createSelector(getHistoryState, getVersionIds, fromHistory.selectAllVersions); +export const getSelectedVersion = createSelector(getVersionEntities, getSelectedVersionId, getInCollectionFn); +export const getSelectedVersionNumber = createSelector( + getVersionCollection, + getSelectedVersionId, + (versions, selectedId) => versions.indexOf(versions.find(v => v.id === selectedId)) + 1 +); + +export const getSelectedIsCurrent = createSelector( + getSelectedVersion, + getVersionCollection, + (selected, collection) => { + return selected ? collection[0].id === selected.id : null; + } +); diff --git a/ui/src/app/metadata/configuration/service/configuration.service.spec.ts b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts index b3588c174..3d44cae6a 100644 --- a/ui/src/app/metadata/configuration/service/configuration.service.spec.ts +++ b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts @@ -1,9 +1,10 @@ import { TestBed, async, inject } from '@angular/core/testing'; import { HttpClientModule, HttpRequest } from '@angular/common/http'; import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; -import { MetadataConfigurationService, PATHS } from './configuration.service'; +import { MetadataConfigurationService } from './configuration.service'; import { FileBackedHttpMetadataProviderEditor } from '../../provider/model'; import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; +import { PATHS } from '../configuration.values'; describe(`Attributes Service`, () => { beforeEach(() => { diff --git a/ui/src/app/metadata/configuration/service/configuration.service.ts b/ui/src/app/metadata/configuration/service/configuration.service.ts index 2f0420c49..ee0093b36 100644 --- a/ui/src/app/metadata/configuration/service/configuration.service.ts +++ b/ui/src/app/metadata/configuration/service/configuration.service.ts @@ -6,15 +6,7 @@ import { Wizard } from '../../../wizard/model'; import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; import { MetadataProviderEditorTypes } from '../../provider/model'; import { Schema } from '../model/schema'; - -export enum PATHS { - resolver = 'EntityDescriptor', - provider = 'MetadataResolvers' -} - -export const DEFINITIONS = { - resolver: MetadataSourceEditor -}; +import { PATHS } from '../configuration.values'; @Injectable() export class MetadataConfigurationService { diff --git a/ui/src/app/metadata/version/service/history.service.spec.ts b/ui/src/app/metadata/configuration/service/history.service.spec.ts similarity index 65% rename from ui/src/app/metadata/version/service/history.service.spec.ts rename to ui/src/app/metadata/configuration/service/history.service.spec.ts index 71e6ec960..0e8bc77a4 100644 --- a/ui/src/app/metadata/version/service/history.service.spec.ts +++ b/ui/src/app/metadata/configuration/service/history.service.spec.ts @@ -1,7 +1,7 @@ import { TestBed, async, inject } from '@angular/core/testing'; import { HttpClientModule } from '@angular/common/http'; import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; -import { HistoryService } from './history.service'; +import { MetadataHistoryService } from './history.service'; describe(`Attributes Service`, () => { beforeEach(() => { @@ -11,15 +11,15 @@ describe(`Attributes Service`, () => { HttpClientTestingModule ], providers: [ - HistoryService + MetadataHistoryService ] }); }); describe('query method', () => { - it(`should return a MetadataHistory`, async(inject([HistoryService, HttpTestingController], - (service: HistoryService) => { - service.query().subscribe(history => { + it(`should return a MetadataHistory`, async(inject([MetadataHistoryService, HttpTestingController], + (service: MetadataHistoryService) => { + service.query('foo', 'resolver').subscribe(history => { expect(history).toBeDefined(); }); } diff --git a/ui/src/app/metadata/configuration/service/history.service.ts b/ui/src/app/metadata/configuration/service/history.service.ts new file mode 100644 index 000000000..3125c2091 --- /dev/null +++ b/ui/src/app/metadata/configuration/service/history.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable, of } from 'rxjs'; +import { MetadataHistory } from '../model/history'; + +import { PATHS } from '../../configuration/configuration.values'; +import { MetadataVersion } from '../model/version'; +import { map } from 'rxjs/operators'; + +@Injectable() +export class MetadataHistoryService { + + readonly base = `api`; + readonly path = `Versions`; + + constructor( + private http: HttpClient + ) { } + + query(resourceId: string, type: string): Observable { + return this.http.get(`/${this.base}/${PATHS[type]}/${resourceId}/${this.path}`).pipe( + map(resp => ({ + versions: resp + })) + ); + } +} diff --git a/ui/src/app/metadata/metadata.module.ts b/ui/src/app/metadata/metadata.module.ts index 231f39f5f..3712ba969 100644 --- a/ui/src/app/metadata/metadata.module.ts +++ b/ui/src/app/metadata/metadata.module.ts @@ -11,7 +11,6 @@ import { I18nModule } from '../i18n/i18n.module'; import { CustomWidgetRegistry } from '../schema-form/registry'; import { WidgetRegistry, SchemaValidatorFactory } from 'ngx-schema-form'; import { CustomSchemaValidatorFactory } from '../schema-form/service/schema-validator'; -import { MetadataVersionModule } from './version/version.module'; import { MetadataConfigurationModule } from './configuration/configuration.module'; @NgModule({ @@ -21,7 +20,6 @@ import { MetadataConfigurationModule } from './configuration/configuration.modul DomainModule.forRoot(), ManagerModule.forRoot(), ProviderModule.forRoot(), - MetadataVersionModule.forRoot(), MetadataConfigurationModule.forRoot(), MetadataRoutingModule, I18nModule diff --git a/ui/src/app/metadata/metadata.routing.ts b/ui/src/app/metadata/metadata.routing.ts index 35902415c..a2ef97557 100644 --- a/ui/src/app/metadata/metadata.routing.ts +++ b/ui/src/app/metadata/metadata.routing.ts @@ -5,7 +5,6 @@ import { MetadataPageComponent } from './metadata.component'; import { ResolverRoutes } from './resolver/resolver.routing'; import { ProviderRoutes } from './provider/provider.routing'; import { ManagerRoutes } from './manager/manager.routing'; -import { VersionRoutes } from './version/version.routing'; import { ConfigurationRoutes } from './configuration/configuration.routing'; const routes: Routes = [ @@ -16,7 +15,6 @@ const routes: Routes = [ ...ManagerRoutes, ...ResolverRoutes, ...ProviderRoutes, - ...VersionRoutes, ...ConfigurationRoutes ], }, diff --git a/ui/src/app/metadata/resolver/container/resolver-edit.component.ts b/ui/src/app/metadata/resolver/container/resolver-edit.component.ts index f793ef96e..775c956d3 100644 --- a/ui/src/app/metadata/resolver/container/resolver-edit.component.ts +++ b/ui/src/app/metadata/resolver/container/resolver-edit.component.ts @@ -81,7 +81,7 @@ export class ResolverEditComponent implements OnDestroy, CanComponentDeactivate cancel(): void { this.clear(); - this.router.navigate(['dashboard', 'metadata', 'manager', 'resolvers']); + this.router.navigate(['metadata', 'resolver', this.resolver.id, 'configuration']); } canDeactivate( diff --git a/ui/src/app/metadata/resolver/effect/collection.effects.ts b/ui/src/app/metadata/resolver/effect/collection.effects.ts index f3b4a4ac8..e5f1edd94 100644 --- a/ui/src/app/metadata/resolver/effect/collection.effects.ts +++ b/ui/src/app/metadata/resolver/effect/collection.effects.ts @@ -84,7 +84,7 @@ export class ResolverCollectionEffects { updateResolverSuccessRedirect$ = this.actions$.pipe( ofType(ResolverCollectionActionTypes.UPDATE_RESOLVER_SUCCESS), map(action => action.payload), - tap(provider => this.router.navigate(['dashboard'])) + tap(provider => this.router.navigate(['metadata', 'resolver', provider.id, 'configuration'])) ); @Effect() diff --git a/ui/src/app/metadata/version/container/version-history.component.html b/ui/src/app/metadata/version/container/version-history.component.html deleted file mode 100644 index 099841308..000000000 --- a/ui/src/app/metadata/version/container/version-history.component.html +++ /dev/null @@ -1,18 +0,0 @@ -
-
-
-
-
- - - Metadata resolver history - -
-
-
-
- -
-
-
diff --git a/ui/src/app/metadata/version/container/version-history.component.ts b/ui/src/app/metadata/version/container/version-history.component.ts deleted file mode 100644 index dc26aa1fa..000000000 --- a/ui/src/app/metadata/version/container/version-history.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Component, ChangeDetectionStrategy } from '@angular/core'; -import { MetadataHistory } from '../model/history'; -import { HistoryService } from '../service/history.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'version-history', - changeDetection: ChangeDetectionStrategy.OnPush, - templateUrl: './version-history.component.html', - styleUrls: [] -}) -export class VersionHistoryComponent { - - history$: Observable; - - constructor( - private historyService: HistoryService - ) { - this.history$ = this.historyService.query(); - } -} diff --git a/ui/src/app/metadata/version/model/version.ts b/ui/src/app/metadata/version/model/version.ts deleted file mode 100644 index 85d1c9c7d..000000000 --- a/ui/src/app/metadata/version/model/version.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface MetadataVersion { - versionNumber: Number; - saveDate: Date; - changedBy: String; - actions: string[]; -} diff --git a/ui/src/app/metadata/version/service/history.service.ts b/ui/src/app/metadata/version/service/history.service.ts deleted file mode 100644 index acf22b2ff..000000000 --- a/ui/src/app/metadata/version/service/history.service.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; -import { MetadataHistory } from '../model/history'; - - -@Injectable() -export class HistoryService { - - readonly base = '/api'; - - constructor( - private http: HttpClient - ) { } - - query(): Observable { - return of({ - versions: [ - { - versionNumber: 1, - saveDate: new Date(), - changedBy: 'admin', - actions: [] - }, - { - versionNumber: 2, - saveDate: new Date(), - changedBy: 'admin', - actions: ['restore'] - } - ] - }); - } -} diff --git a/ui/src/app/metadata/version/version.module.ts b/ui/src/app/metadata/version/version.module.ts deleted file mode 100644 index 2e3d36fb5..000000000 --- a/ui/src/app/metadata/version/version.module.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NgModule, ModuleWithProviders } from '@angular/core'; -import { StoreModule } from '@ngrx/store'; -import { EffectsModule } from '@ngrx/effects'; -import { HistoryListComponent } from './component/history-list.component'; -import { CommonModule } from '@angular/common'; -import { VersionHistoryComponent } from './container/version-history.component'; -import { HistoryService } from './service/history.service'; -import { I18nModule } from '../../i18n/i18n.module'; - -@NgModule({ - declarations: [ - HistoryListComponent, - VersionHistoryComponent - ], - entryComponents: [], - imports: [ - CommonModule, - I18nModule - ], - exports: [], - providers: [] -}) -export class MetadataVersionModule { - static forRoot(): ModuleWithProviders { - return { - ngModule: RootMetadataVersionModule, - providers: [] - }; - } -} - -@NgModule({ - imports: [ - MetadataVersionModule, - // StoreModule.forFeature('resolver', fromResolver.reducers), - // EffectsModule.forFeature([]) - ], - providers: [ - HistoryService - ] -}) -export class RootMetadataVersionModule { } diff --git a/ui/src/app/metadata/version/version.routing.ts b/ui/src/app/metadata/version/version.routing.ts deleted file mode 100644 index dfb76468d..000000000 --- a/ui/src/app/metadata/version/version.routing.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Routes } from '@angular/router'; -import { VersionHistoryComponent } from './container/version-history.component'; - -export const VersionRoutes: Routes = [ - { - path: ':type/:id/versions', - component: VersionHistoryComponent - } -];