diff --git a/ui/package.json b/ui/package.json index 6cfe807cd..60a73244f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -6,7 +6,7 @@ "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", "build": "ng build", - "test": "ng test --code-coverage --source-map=false", + "test": "ng test --code-coverage --source-map=true", "lint": "ng lint", "e2e": "ng e2e", "build:static": "node-sass src/static.scss ./dist/unsecured/static.css", diff --git a/ui/src/app/metadata/configuration/effect/configuration.effect.ts b/ui/src/app/metadata/configuration/effect/configuration.effect.ts index ef86d13fd..781d9a7b9 100644 --- a/ui/src/app/metadata/configuration/effect/configuration.effect.ts +++ b/ui/src/app/metadata/configuration/effect/configuration.effect.ts @@ -42,7 +42,7 @@ export class MetadataConfigurationEffects { @Effect() setDefinition$ = this.actions$.pipe( ofType(ConfigurationActionTypes.SET_METADATA), - map(action => new SetDefinition(this.configService.getDefinition(action.payload))) + map(action => new SetDefinition(this.configService.getDefinition(action.payload['@type']))) ); @Effect() 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 b4d485fd0..d02b99560 100644 --- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.spec.ts @@ -2,12 +2,19 @@ import { reducer } from './configuration.reducer'; import * as fromConfig from './configuration.reducer'; import * as actions from '../action/configuration.action'; import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; -import { SCHEMA } from '../../../../testing/form-schema.stub'; +import { SCHEMA as schema } from '../../../../testing/form-schema.stub'; import { MetadataResolver } from '../../domain/model'; describe('Configuration Reducer', () => { const initialState: fromConfig.State = { ...fromConfig.initialState }; + const definition = new MetadataSourceEditor(); + const model: MetadataResolver = { + id: 'foo', + serviceProviderName: 'foo', + '@type': 'MetadataResolver' + }; + describe('undefined action', () => { it('should return the default state', () => { const result = reducer(undefined, {} as any); @@ -18,7 +25,6 @@ describe('Configuration Reducer', () => { describe('SET_DEFINITION action', () => { it('should set the state definition', () => { - const definition = new MetadataSourceEditor(); const action = new actions.SetDefinition(definition); const result = reducer(initialState, action); @@ -28,24 +34,33 @@ describe('Configuration Reducer', () => { describe('SET_SCHEMA action', () => { it('should set the state schema', () => { - const action = new actions.SetSchema(SCHEMA); + const action = new actions.SetSchema(schema); const result = reducer(initialState, action); - expect(result).toEqual({ ...initialState, schema: SCHEMA }); + expect(result).toEqual({ ...initialState, schema }); }); }); describe('SET_METADATA action', () => { it('should set the state metadata model', () => { - const model: MetadataResolver = { - id: 'foo', - serviceProviderName: 'foo', - '@type': 'MetadataResolver' - }; const action = new actions.SetMetadata(model as MetadataResolver); const result = reducer(initialState, action); expect(result).toEqual({ ...initialState, model }); }); }); + + describe('CLEAR action', () => { + it('should clear the state and reset to initial state', () => { + const action = new actions.ClearConfiguration(); + const result = reducer({ + ...initialState, + model, + definition, + schema + }, action); + + expect(result).toEqual(initialState); + }); + }); }); diff --git a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts index c526aa22b..966cf634c 100644 --- a/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts +++ b/ui/src/app/metadata/configuration/reducer/configuration.reducer.ts @@ -1,6 +1,6 @@ import { ConfigurationActionTypes, ConfigurationActionsUnion } from '../action/configuration.action'; import { Metadata } from '../../domain/domain.type'; -import { Wizard, WizardStep } from '../../../wizard/model'; +import { Wizard } from '../../../wizard/model'; import { Schema } from '../model/schema'; export interface State { diff --git a/ui/src/app/metadata/configuration/service/configuration.service.spec.ts b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts new file mode 100644 index 000000000..b3588c174 --- /dev/null +++ b/ui/src/app/metadata/configuration/service/configuration.service.spec.ts @@ -0,0 +1,63 @@ +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 { FileBackedHttpMetadataProviderEditor } from '../../provider/model'; +import { MetadataSourceEditor } from '../../domain/model/wizards/metadata-source-editor'; + +describe(`Attributes Service`, () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + HttpClientModule, + HttpClientTestingModule + ], + providers: [ + MetadataConfigurationService + ] + }); + }); + + describe('find method', () => { + it(`should send an expected GET request`, async(inject([MetadataConfigurationService, HttpTestingController], + (service: MetadataConfigurationService, backend: HttpTestingController) => { + const type = 'resolver'; + const id = 'foo'; + service.find(id, type).subscribe(); + backend.expectOne((req: HttpRequest) => { + return req.url === `${service.base}/${PATHS[type]}/${id}` + && req.method === 'GET'; + }, `GET metadata by id and type`); + } + ))); + }); + + describe('loadSchema method', () => { + it(`should send an expected GET request`, async(inject([MetadataConfigurationService, HttpTestingController], + (service: MetadataConfigurationService, backend: HttpTestingController) => { + const path = '/foo.json'; + service.loadSchema(path).subscribe(); + backend.expectOne((req: HttpRequest) => { + return req.url === `${path}` + && req.method === 'GET'; + }, `GET schema by path`); + } + ))); + }); + + describe('getDefinition method', () => { + it(`should retrieve the editor definition by model type`, async(inject([MetadataConfigurationService, HttpTestingController], + (service: MetadataConfigurationService, backend: HttpTestingController) => { + const def = service.getDefinition('FileBackedHttpMetadataResolver'); + expect(def).toBe(FileBackedHttpMetadataProviderEditor); + } + ))); + + it(`should instantiate an editor for resolvers`, async(inject([MetadataConfigurationService], + (service: MetadataConfigurationService) => { + const def = service.getDefinition('foo'); + expect(def instanceof MetadataSourceEditor).toBe(true); + } + ))); + }); +}); diff --git a/ui/src/app/metadata/configuration/service/configuration.service.ts b/ui/src/app/metadata/configuration/service/configuration.service.ts index d7f5641d9..2f0420c49 100644 --- a/ui/src/app/metadata/configuration/service/configuration.service.ts +++ b/ui/src/app/metadata/configuration/service/configuration.service.ts @@ -29,8 +29,8 @@ export class MetadataConfigurationService { return this.http.get(`${this.base}/${PATHS[type]}/${id}`); } - getDefinition(model: Metadata): Wizard { - return MetadataProviderEditorTypes.find(def => def.type === model['@type']) || new MetadataSourceEditor(); + getDefinition(type: string): Wizard { + return MetadataProviderEditorTypes.find(def => def.type === type) || new MetadataSourceEditor(); } loadSchema(path: string): Observable { diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts b/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts index 335cd634f..107ef7241 100644 --- a/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts +++ b/ui/src/app/metadata/domain/component/wizard-summary.component.spec.ts @@ -4,7 +4,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { NgbDropdownModule, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap'; -import { getStepProperties, WizardSummaryComponent } from './wizard-summary.component'; +import { WizardSummaryComponent } from './wizard-summary.component'; import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from 'ngx-schema-form'; import { Wizard } from '../../../wizard/model'; import { MetadataProvider } from '../../domain/model'; @@ -68,17 +68,6 @@ describe('Provider Wizard Summary Component', () => { expect(app).toBeTruthy(); })); - describe('getStepProperties function', () => { - it('should return an empty array of schema or schema.properties is not defined', () => { - expect(getStepProperties(null, {})).toEqual([]); - expect(getStepProperties({}, {})).toEqual([]); - }); - - it('should return a formatted list of properties', () => { - expect(getStepProperties(SCHEMA, {}).length).toBe(2); - }); - }); - describe('gotoPage function', () => { it('should emit an empty string if page is null', () => { spyOn(app.onPageSelect, 'emit'); diff --git a/ui/src/app/metadata/domain/component/wizard-summary.component.ts b/ui/src/app/metadata/domain/component/wizard-summary.component.ts index fa570a1c0..e8b8deb34 100644 --- a/ui/src/app/metadata/domain/component/wizard-summary.component.ts +++ b/ui/src/app/metadata/domain/component/wizard-summary.component.ts @@ -1,10 +1,11 @@ import { Component, Input, SimpleChanges, OnChanges, Output, EventEmitter } from '@angular/core'; +import merge from 'deepmerge'; import { Wizard, WizardStep } from '../../../wizard/model'; import { MetadataProvider, MetadataResolver } from '../../domain/model'; import { Property } from '../model/property'; import { getSplitSchema } from '../../../wizard/reducer'; -import merge from 'deepmerge'; +import { getStepProperties } from '../utility/configuration'; interface Section { id: string; @@ -14,47 +15,6 @@ interface Section { properties: Property[]; } -export function getDefinition(path: string, definitions: any): any { - let def = path.split('/').pop(); - return definitions[def]; -} - -export function getPropertyItemSchema(items: any, definitions: any): any { - if (!items) { return null; } - return items.$ref ? getDefinition(items.$ref, definitions) : items; -} - -export function getStepProperty(property, model, definitions): Property { - if (!property) { return null; } - property = property.$ref ? { ...property, ...getDefinition(property.$ref, definitions) } : property; - return { - name: property.title, - value: model, - type: property.type, - items: getPropertyItemSchema(property.items, definitions), - properties: getStepProperties( - property, - model, - definitions - ), - widget: property.widget instanceof String ? { id: property.widget } : { ...property.widget } - }; -} - - -export function getStepProperties(schema: any, model: any, definitions: any = {}): Property[] { - if (!schema || !schema.properties) { return []; } - return Object - .keys(schema.properties) - .map(property => { - return getStepProperty( - schema.properties[property], - model && model.hasOwnProperty(property) ? model[property] : null, - definitions - ); - }); -} - @Component({ selector: 'wizard-summary', templateUrl: './wizard-summary.component.html', diff --git a/ui/src/app/metadata/domain/utility/configuration.spec.ts b/ui/src/app/metadata/domain/utility/configuration.spec.ts index e69de29bb..b331a8647 100644 --- a/ui/src/app/metadata/domain/utility/configuration.spec.ts +++ b/ui/src/app/metadata/domain/utility/configuration.spec.ts @@ -0,0 +1,62 @@ +import { getStepProperties, getDefinition, getPropertyItemSchema, getStepProperty } from './configuration'; +import * as utils from './configuration'; +import { SCHEMA } from '../../../../testing/form-schema.stub'; + +describe('domain utility functions', () => { + describe('getStepProperties function', () => { + it('should return an empty array of schema or schema.properties is not defined', () => { + expect(getStepProperties(null, {})).toEqual([]); + expect(getStepProperties({}, {})).toEqual([]); + }); + + it('should return a formatted list of properties', () => { + expect(getStepProperties(SCHEMA, {}).length).toBe(2); + }); + }); + + describe('getDefinitions method', () => { + it('should retrieve the definitions from the json schema', () => { + const definition = { + id: 'foo', + title: 'bar', + description: 'baz', + type: 'string' + }; + expect(getDefinition('/foo/bar', {bar: definition})).toBe(definition); + }); + }); + + describe('getPropertyItemSchema method', () => { + it('should return null if no items are provided', () => { + expect(getPropertyItemSchema(null, SCHEMA.definitions)).toBeNull(); + }); + it('should retrieve the definitions from the items schema', () => { + expect(getPropertyItemSchema({$ref: 'description'}, SCHEMA.definitions)).toBe(SCHEMA.definitions.description); + }); + it('should return the item itself if no $ref', () => { + let item = {}; + expect(getPropertyItemSchema(item, SCHEMA.definitions)).toBe(item); + }); + }); + + describe('getStepProperty method', () => { + const model = { + name: 'foo', + type: 'bar', + description: 'baz' + }; + it('should return null if no items are provided', () => { + expect(getStepProperty(null, null, SCHEMA.definitions)).toBeNull(); + }); + + it('should retrieve the property $ref definition if available', () => { + const property = getStepProperty( + { $ref: 'description' }, + model, + SCHEMA.definitions + ); + expect(property.type).toBe('string'); + }); + }); +}); + diff --git a/ui/src/app/metadata/domain/utility/configuration.ts b/ui/src/app/metadata/domain/utility/configuration.ts index 0cd0a7a45..dc641c4dd 100644 --- a/ui/src/app/metadata/domain/utility/configuration.ts +++ b/ui/src/app/metadata/domain/utility/configuration.ts @@ -4,6 +4,7 @@ export function getDefinition(path: string, definitions: any): any { let def = path.split('/').pop(); return definitions[def]; } + export function getPropertyItemSchema(items: any, definitions: any): any { if (!items) { return null; } return items.$ref ? getDefinition(items.$ref, definitions) : items; @@ -26,6 +27,7 @@ export function getStepProperty(property, model, definitions): Property { }; } + export function getStepProperties(schema: any, model: any, definitions: any = {}): Property[] { if (!schema || !schema.properties) { return []; } return Object diff --git a/ui/src/testing/form-schema.stub.ts b/ui/src/testing/form-schema.stub.ts index c91f63bf1..f317b1199 100644 --- a/ui/src/testing/form-schema.stub.ts +++ b/ui/src/testing/form-schema.stub.ts @@ -44,5 +44,13 @@ export const SCHEMA = { '@type' ] } - ] + ], + 'definitions': { + 'description': { + 'title': 'Description', + 'description': 'A description of the object', + 'type': 'string', + 'widget': 'string' + } + } };