diff --git a/ui/src/app/metadata/configuration/action/configuration.action.ts b/ui/src/app/metadata/configuration/action/configuration.action.ts
index 6c71b61a6..1aa442601 100644
--- a/ui/src/app/metadata/configuration/action/configuration.action.ts
+++ b/ui/src/app/metadata/configuration/action/configuration.action.ts
@@ -12,9 +12,17 @@ export enum ConfigurationActionTypes {
LOAD_SCHEMA_SUCCESS = '[Metadata Configuration] Load Schema Success',
LOAD_SCHEMA_ERROR = '[Metadata Configuration] Load Schema Error',
+ LOAD_XML_REQUEST = '[Metadata Configuration] Load XML Request',
+ LOAD_XML_SUCCESS = '[Metadata Configuration] Load XML Success',
+ LOAD_XML_ERROR = '[Metadata Configuration] Load XML Error',
+
SET_METADATA = '[Metadata Configuration] Set Metadata Model',
SET_DEFINITION = '[Metadata Configuration] Set Metadata Definition',
SET_SCHEMA = '[Metadata Configuration] Set Metadata Schema',
+ SET_XML = '[Metadata Configuration] Set Metadata Xml',
+
+ DOWNLOAD_XML = '[Metadata Configuration] Download Metadata Xml',
+
CLEAR = '[Metadata Configuration] Clear'
}
@@ -54,6 +62,24 @@ export class LoadSchemaError implements Action {
constructor(public payload: any) { }
}
+export class LoadXmlRequest implements Action {
+ readonly type = ConfigurationActionTypes.LOAD_XML_REQUEST;
+
+ constructor(public payload: string) { }
+}
+
+export class LoadXmlSuccess implements Action {
+ readonly type = ConfigurationActionTypes.LOAD_XML_SUCCESS;
+
+ constructor(public payload: string) { }
+}
+
+export class LoadXmlError implements Action {
+ readonly type = ConfigurationActionTypes.LOAD_XML_ERROR;
+
+ constructor(public payload: any) { }
+}
+
export class SetMetadata implements Action {
readonly type = ConfigurationActionTypes.SET_METADATA;
@@ -72,6 +98,16 @@ export class SetSchema implements Action {
constructor(public payload: Schema) { }
}
+export class SetXml implements Action {
+ readonly type = ConfigurationActionTypes.SET_XML;
+
+ constructor(public payload: string) { }
+}
+
+export class DownloadXml implements Action {
+ readonly type = ConfigurationActionTypes.DOWNLOAD_XML;
+}
+
export class ClearConfiguration implements Action {
readonly type = ConfigurationActionTypes.CLEAR;
}
@@ -83,7 +119,12 @@ export type ConfigurationActionsUnion =
| LoadSchemaRequest
| LoadSchemaSuccess
| LoadSchemaError
+ | LoadXmlRequest
+ | LoadXmlSuccess
+ | LoadXmlError
| SetMetadata
| SetDefinition
| SetSchema
+ | SetXml
+ | DownloadXml
| ClearConfiguration;
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 136e625d7..258b9aa77 100644
--- a/ui/src/app/metadata/configuration/component/metadata-configuration.component.html
+++ b/ui/src/app/metadata/configuration/component/metadata-configuration.component.html
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
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 a61db7b7c..c399fbacf 100644
--- a/ui/src/app/metadata/configuration/container/configuration.component.spec.ts
+++ b/ui/src/app/metadata/configuration/container/configuration.component.spec.ts
@@ -9,14 +9,6 @@ import { ConfigurationComponent } from './configuration.component';
import * as fromConfiguration from '../reducer';
import { MockI18nModule } from '../../../../testing/i18n.stub';
-@Component({
- selector: 'metadata-configuration',
- template: ``
-})
-class MetadataConfigurationComponent {
- @Input() configuration: MetadataConfiguration;
-}
-
@Component({
template: `
@@ -47,7 +39,6 @@ describe('Metadata Configuration Page Component', () => {
],
declarations: [
ConfigurationComponent,
- MetadataConfigurationComponent,
TestHostComponent
],
}).compileComponents();
diff --git a/ui/src/app/metadata/configuration/container/configuration.component.ts b/ui/src/app/metadata/configuration/container/configuration.component.ts
index e22f23861..d18e5ab34 100644
--- a/ui/src/app/metadata/configuration/container/configuration.component.ts
+++ b/ui/src/app/metadata/configuration/container/configuration.component.ts
@@ -1,7 +1,7 @@
import { Store } from '@ngrx/store';
import { Component, ChangeDetectionStrategy, OnDestroy } from '@angular/core';
import { Observable, Subject } from 'rxjs';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Params } from '@angular/router';
import * as fromConfiguration from '../reducer';
import { MetadataConfiguration } from '../model/metadata-configuration';
@@ -17,14 +17,10 @@ import { LoadMetadataRequest, ClearConfiguration } from '../action/configuration
export class ConfigurationComponent implements OnDestroy {
private ngUnsubscribe: Subject
= new Subject();
- configuration$: Observable;
-
constructor(
private store: Store,
private routerState: ActivatedRoute
) {
- this.configuration$ = this.store.select(fromConfiguration.getConfigurationSections);
-
this.routerState.params.pipe(
takeUntil(this.ngUnsubscribe),
map(params => new LoadMetadataRequest({id: params.id, type: params.type}))
diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.html b/ui/src/app/metadata/configuration/container/metadata-options.component.html
new file mode 100644
index 000000000..5e6b85384
--- /dev/null
+++ b/ui/src/app/metadata/configuration/container/metadata-options.component.html
@@ -0,0 +1,3 @@
+
+
+
\ 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
new file mode 100644
index 000000000..2e35d8c75
--- /dev/null
+++ b/ui/src/app/metadata/configuration/container/metadata-options.component.spec.ts
@@ -0,0 +1,70 @@
+import { Component, ViewChild, Input } from '@angular/core';
+import { TestBed, async, ComponentFixture } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { StoreModule, combineReducers, Store } from '@ngrx/store';
+import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { MetadataConfiguration } from '../model/metadata-configuration';
+import * as fromConfiguration from '../reducer';
+import { MockI18nModule } from '../../../../testing/i18n.stub';
+import { MetadataOptionsComponent } from './metadata-options.component';
+
+@Component({
+ selector: 'metadata-configuration',
+ template: ``
+})
+class MetadataConfigurationComponent {
+ @Input() configuration: MetadataConfiguration;
+}
+
+@Component({
+ template: `
+
+ `
+})
+class TestHostComponent {
+ @ViewChild(MetadataOptionsComponent)
+ public componentUnderTest: MetadataOptionsComponent;
+
+ configuration: MetadataConfiguration = { sections: [] };
+}
+
+describe('Metadata Options Page Component', () => {
+
+ let fixture: ComponentFixture;
+ let instance: TestHostComponent;
+ let app: MetadataOptionsComponent;
+ let store: Store;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ NgbDropdownModule,
+ StoreModule.forRoot({
+ 'metadata-configuration': combineReducers(fromConfiguration.reducers),
+ }),
+ MockI18nModule,
+ RouterTestingModule
+ ],
+ declarations: [
+ MetadataOptionsComponent,
+ MetadataConfigurationComponent,
+ TestHostComponent
+ ],
+ }).compileComponents();
+
+ store = TestBed.get(Store);
+ spyOn(store, 'dispatch');
+ spyOn(store, 'select');
+
+ fixture = TestBed.createComponent(TestHostComponent);
+ instance = fixture.componentInstance;
+ app = instance.componentUnderTest;
+ fixture.detectChanges();
+ }));
+
+ it('should load metadata objects', async(() => {
+ expect(app).toBeTruthy();
+ expect(store.select).toHaveBeenCalled();
+ }));
+});
diff --git a/ui/src/app/metadata/configuration/container/metadata-options.component.ts b/ui/src/app/metadata/configuration/container/metadata-options.component.ts
new file mode 100644
index 000000000..bc2d0bb0d
--- /dev/null
+++ b/ui/src/app/metadata/configuration/container/metadata-options.component.ts
@@ -0,0 +1,23 @@
+import { Store } from '@ngrx/store';
+import { Component, ChangeDetectionStrategy } from '@angular/core';
+import { Observable } from 'rxjs';
+
+import * as fromConfiguration from '../reducer';
+import { MetadataConfiguration } from '../model/metadata-configuration';
+
+@Component({
+ selector: 'metadata-options-page',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ templateUrl: './metadata-options.component.html',
+ styleUrls: []
+})
+export class MetadataOptionsComponent {
+
+ configuration$: Observable;
+
+ constructor(
+ private store: Store
+ ) {
+ this.configuration$ = this.store.select(fromConfiguration.getConfigurationSections);
+ }
+}
diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.html b/ui/src/app/metadata/configuration/container/metadata-xml.component.html
new file mode 100644
index 000000000..c76b4627b
--- /dev/null
+++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.html
@@ -0,0 +1,7 @@
+
+
{{ xml$ | async }}
+
+
\ No newline at end of file
diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.spec.ts b/ui/src/app/metadata/configuration/container/metadata-xml.component.spec.ts
new file mode 100644
index 000000000..81fbd836f
--- /dev/null
+++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.spec.ts
@@ -0,0 +1,61 @@
+import { Component, ViewChild, Input } from '@angular/core';
+import { TestBed, async, ComponentFixture } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { StoreModule, combineReducers, Store } from '@ngrx/store';
+import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
+
+import { MetadataConfiguration } from '../model/metadata-configuration';
+import * as fromConfiguration from '../reducer';
+import { MockI18nModule } from '../../../../testing/i18n.stub';
+import { MetadataXmlComponent } from './metadata-xml.component';
+
+@Component({
+ template: `
+
+ `
+})
+class TestHostComponent {
+ @ViewChild(MetadataXmlComponent)
+ public componentUnderTest: MetadataXmlComponent;
+
+ configuration: MetadataConfiguration = { sections: [] };
+}
+
+describe('Metadata Xml Page Component', () => {
+
+ let fixture: ComponentFixture;
+ let instance: TestHostComponent;
+ let app: MetadataXmlComponent;
+ let store: Store;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ NgbDropdownModule,
+ StoreModule.forRoot({
+ 'metadata-configuration': combineReducers(fromConfiguration.reducers),
+ }),
+ MockI18nModule,
+ RouterTestingModule
+ ],
+ declarations: [
+ MetadataXmlComponent,
+ TestHostComponent
+ ],
+ }).compileComponents();
+
+ store = TestBed.get(Store);
+ spyOn(store, 'dispatch');
+ spyOn(store, 'select');
+
+ fixture = TestBed.createComponent(TestHostComponent);
+ instance = fixture.componentInstance;
+ app = instance.componentUnderTest;
+ fixture.detectChanges();
+ }));
+
+ it('should load metadata objects', async(() => {
+ expect(app).toBeTruthy();
+ expect(store.select).toHaveBeenCalledTimes(2);
+ }));
+});
diff --git a/ui/src/app/metadata/configuration/container/metadata-xml.component.ts b/ui/src/app/metadata/configuration/container/metadata-xml.component.ts
new file mode 100644
index 000000000..26b62323e
--- /dev/null
+++ b/ui/src/app/metadata/configuration/container/metadata-xml.component.ts
@@ -0,0 +1,33 @@
+import { Store } from '@ngrx/store';
+import { Component } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+
+import * as fromConfiguration from '../reducer';
+import { Metadata } from '../../domain/domain.type';
+import { DownloadXml } from '../action/configuration.action';
+
+@Component({
+ selector: 'metadata-xml-page',
+ templateUrl: './metadata-xml.component.html',
+ styleUrls: []
+})
+export class MetadataXmlComponent {
+
+ private ngUnsubscribe: Subject = new Subject();
+
+ entity: Metadata;
+ entity$: Observable;
+ xml: string;
+ xml$: Observable;
+
+ constructor(
+ private store: Store
+ ) {
+ this.xml$ = this.store.select(fromConfiguration.getConfigurationXml);
+ this.entity$ = this.store.select(fromConfiguration.getConfigurationModel);
+ }
+
+ preview(): void {
+ this.store.dispatch(new DownloadXml());
+ }
+}
diff --git a/ui/src/app/metadata/configuration/effect/configuration.effect.ts b/ui/src/app/metadata/configuration/effect/configuration.effect.ts
index 781d9a7b9..ff76c8125 100644
--- a/ui/src/app/metadata/configuration/effect/configuration.effect.ts
+++ b/ui/src/app/metadata/configuration/effect/configuration.effect.ts
@@ -1,7 +1,9 @@
import { Injectable } from '@angular/core';
import { Effect, Actions, ofType } from '@ngrx/effects';
-import { switchMap, catchError, map } from 'rxjs/operators';
-import { of } from 'rxjs';
+import { switchMap, catchError, map, tap, withLatestFrom } from 'rxjs/operators';
+import { of, Observable } from 'rxjs';
+import * as FileSaver from 'file-saver';
+import { Store } from '@ngrx/store';
import { MetadataConfigurationService } from '../service/configuration.service';
import {
@@ -14,8 +16,17 @@ import {
LoadSchemaRequest,
LoadSchemaSuccess,
SetSchema,
- LoadSchemaError
+ LoadSchemaError,
+ LoadXmlSuccess,
+ LoadXmlError,
+ SetXml,
+ DownloadXml
} from '../action/configuration.action';
+import { ResolverService } from '../../domain/service/resolver.service';
+import { EntityIdService } from '../../domain/service/entity-id.service';
+import { State } from '../reducer/configuration.reducer';
+import { getConfigurationModel, getConfigurationXml } from '../reducer';
+import { MetadataResolver } from '../../domain/model';
@Injectable()
export class MetadataConfigurationEffects {
@@ -33,6 +44,33 @@ export class MetadataConfigurationEffects {
)
);
+ @Effect()
+ loadMetadataXml$ = this.actions$.pipe(
+ ofType(ConfigurationActionTypes.LOAD_METADATA_REQUEST),
+ switchMap(action => {
+ let loader: Observable;
+ switch (action.payload.type) {
+ case 'filter':
+ loader = this.entityService.preview(action.payload.id);
+ break;
+ default:
+ loader = this.providerService.preview(action.payload.id);
+ break;
+ }
+
+ return loader.pipe(
+ map(xml => new LoadXmlSuccess(xml)),
+ catchError(error => of(new LoadXmlError(error)))
+ );
+ })
+ );
+
+ @Effect()
+ setXmlOnLoad$ = this.actions$.pipe(
+ ofType(ConfigurationActionTypes.LOAD_XML_SUCCESS),
+ map(action => new SetXml(action.payload))
+ );
+
@Effect()
setMetadataOnLoad$ = this.actions$.pipe(
ofType(ConfigurationActionTypes.LOAD_METADATA_SUCCESS),
@@ -70,8 +108,25 @@ export class MetadataConfigurationEffects {
map(action => new SetSchema(action.payload))
);
+ @Effect({dispatch: false})
+ downloadXml$ = this.actions$.pipe(
+ ofType(ConfigurationActionTypes.DOWNLOAD_XML),
+ withLatestFrom(
+ this.store.select(getConfigurationModel),
+ this.store.select(getConfigurationXml)
+ ),
+ tap(([action, entity, xml]) => {
+ const name = entity.name ? entity.name : (entity as MetadataResolver).serviceProviderName;
+ const blob = new Blob([xml], { type: 'text/xml;charset=utf-8' });
+ FileSaver.saveAs(blob, `${name}.xml`);
+ })
+ );
+
constructor(
private configService: MetadataConfigurationService,
- private actions$: Actions
+ private actions$: Actions,
+ private providerService: ResolverService,
+ private entityService: EntityIdService,
+ private store: Store
) { }
}
diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts
index d02b99560..fed39ff3f 100644
--- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts
+++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts
@@ -14,6 +14,7 @@ describe('Configuration Reducer', () => {
serviceProviderName: 'foo',
'@type': 'MetadataResolver'
};
+ const xml = ``;
describe('undefined action', () => {
it('should return the default state', () => {
@@ -50,6 +51,15 @@ describe('Configuration Reducer', () => {
});
});
+ describe('SET_XML action', () => {
+ it('should set the state metadata model', () => {
+ const action = new actions.SetXml(xml);
+ const result = reducer(initialState, action);
+
+ expect(result).toEqual({ ...initialState, xml });
+ });
+ });
+
describe('CLEAR action', () => {
it('should clear the state and reset to initial state', () => {
const action = new actions.ClearConfiguration();
@@ -63,4 +73,33 @@ describe('Configuration Reducer', () => {
expect(result).toEqual(initialState);
});
});
+
+ describe('selector functions', () => {
+ /*
+ export const getModel = (state: State) => state.model;
+ export const getDefinition = (state: State) => state.definition;
+ export const getSchema = (state: State) => state.schema;
+ export const getXml = (state: State) => state.xml;
+ */
+ describe('getModel', () => {
+ it('should retrieve the model from state', () => {
+ expect(fromConfig.getModel({...initialState, model})).toBe(model);
+ });
+ });
+ describe('getDefinition', () => {
+ it('should retrieve the definition from state', () => {
+ expect(fromConfig.getDefinition({ ...initialState, definition })).toBe(definition);
+ });
+ });
+ describe('getSchema', () => {
+ it('should retrieve the schema from state', () => {
+ expect(fromConfig.getSchema({ ...initialState, schema })).toBe(schema);
+ });
+ });
+ describe('getXml', () => {
+ it('should retrieve the schema from state', () => {
+ expect(fromConfig.getXml({ ...initialState, xml })).toBe(xml);
+ });
+ });
+ });
});
diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts
index 966cf634c..a35e20dd7 100644
--- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts
+++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts
@@ -7,12 +7,14 @@ export interface State {
model: Metadata;
schema: Schema;
definition: Wizard;
+ xml: string;
}
export const initialState: State = {
model: null,
schema: null,
- definition: null
+ definition: null,
+ xml: ''
};
export function reducer(state = initialState, action: ConfigurationActionsUnion): State {
@@ -32,6 +34,11 @@ export function reducer(state = initialState, action: ConfigurationActionsUnion)
...state,
model: action.payload
};
+ case ConfigurationActionTypes.SET_XML:
+ return {
+ ...state,
+ xml: action.payload
+ };
case ConfigurationActionTypes.CLEAR:
return {
...initialState
@@ -45,3 +52,4 @@ export function reducer(state = initialState, action: ConfigurationActionsUnion)
export const getModel = (state: State) => state.model;
export const getDefinition = (state: State) => state.definition;
export const getSchema = (state: State) => state.schema;
+export const getXml = (state: State) => state.xml;
diff --git a/ui/src/app/metadata/configuration/reducer/index.ts b/ui/src/app/metadata/configuration/reducer/index.ts
index e8ba98467..d4c2fcc9c 100644
--- a/ui/src/app/metadata/configuration/reducer/index.ts
+++ b/ui/src/app/metadata/configuration/reducer/index.ts
@@ -28,6 +28,7 @@ export const getConfigurationState = createSelector(getState, getConfigurationSt
export const getConfigurationModel = createSelector(getConfigurationState, fromConfiguration.getModel);
export const getConfigurationDefinition = createSelector(getConfigurationState, fromConfiguration.getDefinition);
export const getConfigurationSchema = createSelector(getConfigurationState, fromConfiguration.getSchema);
+export const getConfigurationXml = createSelector(getConfigurationState, fromConfiguration.getXml);
export const getConfigurationSectionsFn = (model, definition, schema) => !definition || !schema ? null :
({
diff --git a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts
index 0c20310c7..4de58be3a 100644
--- a/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts
+++ b/ui/src/app/metadata/manager/container/dashboard-resolvers-list.component.ts
@@ -87,7 +87,8 @@ export class DashboardResolversListComponent implements OnInit {
}
viewConfiguration(entity: MetadataEntity): void {
- this.router.navigate(['metadata', 'resolver', entity.getId(), 'configuration']);
+ // this.store.dispatch(new PreviewEntity({ id: entity.getId(), entity }));
+ this.router.navigate(['metadata', 'resolver', entity.getId(), 'configuration', 'options']);
}
viewMetadataHistory(entity: MetadataEntity): void {