[CORD-1927] Fixing generate url for service models
Change-Id: I08d0d853ba2ab041626e133d079ab4562d73171e
diff --git a/src/app/datasources/helpers/model-discoverer.service.ts b/src/app/datasources/helpers/model-discoverer.service.ts
index c6dcb7e..0addc05 100644
--- a/src/app/datasources/helpers/model-discoverer.service.ts
+++ b/src/app/datasources/helpers/model-discoverer.service.ts
@@ -27,6 +27,7 @@
import {IXosRuntimeStatesService, IXosState} from '../../core/services/runtime-states';
import {IXosModelStoreService} from '../stores/model.store';
import {IXosAuthService} from '../rest/auth.rest';
+import {IXosModeldefsCache} from './modeldefs.service';
export interface IXosModel {
name: string; // the model name
@@ -44,7 +45,6 @@
// Service
export interface IXosModelDiscovererService {
discover(): ng.IPromise<boolean>;
- get(modelName: string): IXosModel;
getApiUrlFromModel(model: IXosModel): string;
areModelsLoaded(): boolean;
}
@@ -59,9 +59,10 @@
'XosNavigationService',
'XosModelStore',
'ngProgressFactory',
- 'AuthService'
+ 'AuthService',
+ 'XosModeldefsCache'
];
- private xosModels: IXosModel[] = []; // list of augmented model definitions;
+
private xosServices: string[] = []; // list of loaded services
private progressBar;
private modelsLoaded: boolean = false;
@@ -75,7 +76,8 @@
private XosNavigationService: IXosNavigationService,
private XosModelStore: IXosModelStoreService,
private ngProgressFactory: any, // check for type defs
- private AuthService: IXosAuthService
+ private AuthService: IXosAuthService,
+ private XosModeldefsCache: IXosModeldefsCache
) {
this.progressBar = this.ngProgressFactory.createInstance();
this.progressBar.setColor('#f6a821');
@@ -85,16 +87,12 @@
return this.modelsLoaded;
}
- public get(modelName: string): IXosModel|null {
- return _.find(this.xosModels, m => m.name === modelName);
- }
-
public getApiUrlFromModel(model: IXosModel): string {
if (model.app === 'core') {
return `/core/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
}
else {
- const serviceName = this.serviceNameFromAppName(model.app);
+ const serviceName = this.XosModeldefsCache.serviceNameFromAppName(model.app);
return `/${serviceName}/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
}
}
@@ -104,7 +102,7 @@
this.progressBar.start();
this.XosModelDefs.get()
.then((modelsDef: IXosModeldef[]) => {
-
+ // TODO store modeldefs and add a method to retrieve the model definition from the name
const pArray = [];
_.forEach(modelsDef, (model: IXosModeldef) => {
this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
@@ -155,12 +153,8 @@
return d.promise;
}
- private serviceNameFromAppName(appName: string): string {
- return appName.replace('services.', '');
- }
-
private stateNameFromModel(model: IXosModel): string {
- return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
+ return `xos.${this.XosModeldefsCache.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
}
private getParentStateFromModel(model: IXosModel): string {
@@ -177,7 +171,7 @@
// add a service state and navigation item if it is not already there
private addService(model: IXosModel): string {
- const serviceName: string = this.serviceNameFromAppName(model.app);
+ const serviceName: string = this.XosModeldefsCache.serviceNameFromAppName(model.app);
if (!_.find(this.xosServices, n => n === serviceName)) {
const serviceState = {
url: serviceName,
@@ -222,7 +216,7 @@
);
// extend model
- model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
+ model.clientUrl = `${this.XosModeldefsCache.serviceNameFromAppName(model.app)}${clientUrl}`;
d.resolve(model);
} catch (e) {
@@ -300,9 +294,7 @@
const d = this.$q.defer();
- if (!_.find(this.xosModels, m => m.name === model.name)) {
- this.xosModels.push(model);
- }
+ this.XosModeldefsCache.cache(model);
d.resolve(model);
diff --git a/src/app/datasources/helpers/model.discoverer.service.spec.ts b/src/app/datasources/helpers/model.discoverer.service.spec.ts
index 7a9e54f..8011442 100644
--- a/src/app/datasources/helpers/model.discoverer.service.spec.ts
+++ b/src/app/datasources/helpers/model.discoverer.service.spec.ts
@@ -1,4 +1,3 @@
-
/*
* Copyright 2017-present Open Networking Foundation
@@ -22,6 +21,7 @@
import {XosModelDiscovererService, IXosModelDiscovererService} from './model-discoverer.service';
import {IXosModeldef} from '../rest/modeldefs.rest';
import {BehaviorSubject} from 'rxjs';
+import {XosModeldefsCache} from './modeldefs.service';
const stubModels: IXosModeldef[] = [
{
@@ -101,7 +101,8 @@
.value('XosModelStore', MockXosModelStore)
.value('ngProgressFactory', MockngProgressFactory)
.value('XosNavigationService', MockXosNavigationService)
- .value('AuthService', {});
+ .value('AuthService', {})
+ .service('XosModeldefsCache', XosModeldefsCache);
angular.mock.module('test');
});
@@ -152,21 +153,6 @@
expect(service.getApiUrlFromModel(model)).toBe('/test/tenants');
});
- it('should retrieve a model definition from local cache', () => {
- const model = {
- name: 'Node',
- app: 'core'
- };
- service['xosModels'] = [
- model
- ];
- expect(service.get('Node')).toEqual(model);
- });
-
- it('should get the service name from the app name', () => {
- expect(service['serviceNameFromAppName']('services.vsg')).toBe('vsg');
- });
-
it('should get the state name from the model', () => {
expect(service['stateNameFromModel']({name: 'Tenant', app: 'services.vsg'})).toBe('xos.vsg.tenant');
});
@@ -276,12 +262,6 @@
);
});
- it('should store the model in memory', () => {
- service['storeModel']({name: 'Tenant'});
- expect(service['xosModels'][0]).toEqual({name: 'Tenant'});
- expect(service['xosModels'].length).toEqual(1);
- });
-
describe('when discovering models', () => {
beforeEach(() => {
spyOn(service, 'cacheModelEntries').and.callThrough();
@@ -296,7 +276,7 @@
service.discover()
.then((res) => {
expect(MockProgressBar.start).toHaveBeenCalled();
- expect(MockXosModelDefs.get).toHaveBeenCalled();
+ expect(MockXosModelDefs.get).toHaveBeenCalled(); // FIXME replace correct spy
expect(service['cacheModelEntries'].calls.count()).toBe(2);
expect(service['addState'].calls.count()).toBe(2);
expect(service['addNavItem'].calls.count()).toBe(2);
diff --git a/src/app/datasources/helpers/modeldefs.service.spec.ts b/src/app/datasources/helpers/modeldefs.service.spec.ts
new file mode 100644
index 0000000..9d5660d
--- /dev/null
+++ b/src/app/datasources/helpers/modeldefs.service.spec.ts
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as angular from 'angular';
+import 'angular-mocks';
+import 'angular-ui-router';
+import {XosModeldefsCache, IXosModeldefsCache} from './modeldefs.service';
+import {IXosModel} from './model-discoverer.service';
+
+describe('The XosModeldefsCache service', () => {
+ let service;
+
+ beforeEach(() => {
+ angular
+ .module('test', [])
+ .service('XosModeldefsCache', XosModeldefsCache);
+
+ angular.mock.module('test');
+ });
+
+ beforeEach(angular.mock.inject((XosModeldefsCache: IXosModeldefsCache) => {
+ service = XosModeldefsCache;
+ }));
+
+ beforeEach(() => {
+ service['xosModelDefs'] = [];
+ });
+
+ describe('the cache method', () => {
+
+ const modelDef: IXosModel = {
+ fields: [
+ {name: 'id', type: 'number'},
+ {name: 'foo', type: 'string'}
+ ],
+ relations: [],
+ name: 'Node',
+ app: 'core',
+ description: '',
+ verbose_name: ''
+ };
+ it('should add a modelDefs', () => {
+ service.cache(modelDef);
+ expect(service['xosModelDefs'].length).toBe(1);
+ });
+
+ it('should not add a modelDefs twice', () => {
+ service.cache(modelDef);
+ service.cache(modelDef);
+ expect(service['xosModelDefs'].length).toBe(1);
+ });
+ });
+
+ it('should get the service name from the app name', () => {
+ expect(service.serviceNameFromAppName('services.vsg')).toBe('vsg');
+ });
+
+ describe('the get method', () => {
+ it('should retrieve a model definition from local cache', () => {
+ const model = {
+ name: 'Node',
+ app: 'core'
+ };
+ service['xosModelDefs'] = [
+ model
+ ];
+ expect(service.get('Node')).toEqual(model);
+ });
+ });
+});
diff --git a/src/app/datasources/helpers/modeldefs.service.ts b/src/app/datasources/helpers/modeldefs.service.ts
new file mode 100644
index 0000000..152b5e8
--- /dev/null
+++ b/src/app/datasources/helpers/modeldefs.service.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as _ from 'lodash';
+import * as pluralize from 'pluralize';
+import {IXosModel} from './model-discoverer.service';
+
+export interface IXosModeldefsCache {
+ cache(modelDef: IXosModel): void;
+ get(modelName: string): IXosModel;
+ getApiUrlFromModel(model: IXosModel): string;
+ serviceNameFromAppName(appName: string): string;
+}
+
+export class XosModeldefsCache implements IXosModeldefsCache {
+
+ private xosModelDefs: IXosModel[] = [];
+
+ public cache(modelDef: IXosModel): void {
+ if (!_.find(this.xosModelDefs, m => m.name === modelDef.name)) {
+ this.xosModelDefs.push(modelDef);
+ }
+ }
+
+ public get(modelName: string): IXosModel|null {
+ return _.find(this.xosModelDefs, m => m.name === modelName);
+ }
+
+ public serviceNameFromAppName(appName: string): string {
+ return appName.replace('services.', '');
+ }
+
+ public getApiUrlFromModel(model: IXosModel): string {
+ // FIXME move pluralize from config to a separate service
+ if (model.app === 'core') {
+ return `/core/${pluralize(model.name.toLowerCase())}`;
+ }
+ else {
+ const serviceName = this.serviceNameFromAppName(model.app);
+ return `/${serviceName}/${pluralize(model.name.toLowerCase())}`;
+ }
+ }
+}
diff --git a/src/app/datasources/helpers/store.helpers.spec.ts b/src/app/datasources/helpers/store.helpers.spec.ts
index 5d0a584..f3b2321 100644
--- a/src/app/datasources/helpers/store.helpers.spec.ts
+++ b/src/app/datasources/helpers/store.helpers.spec.ts
@@ -25,11 +25,13 @@
import {IWSEvent} from '../websocket/global';
import {ConfigHelpers} from '../../core/services/helpers/config.helpers';
import {AuthService} from '../rest/auth.rest';
+import {IXosModeldefsCache} from './modeldefs.service';
let service: IStoreHelpersService;
let subject: BehaviorSubject<any>;
let resource: ng.resource.IResourceClass<any>;
let $resource: ng.resource.IResourceService;
+let xosModelCache: IXosModeldefsCache;
describe('The StoreHelpers service', () => {
@@ -40,6 +42,10 @@
.service('ModelRest', ModelRest) // NOTE evaluate mock
.service('StoreHelpers', StoreHelpers)
.service('AuthService', AuthService)
+ .value('XosModeldefsCache', {
+ get: jasmine.createSpy('XosModeldefsCache.get'),
+ getApiUrlFromModel: jasmine.createSpy('XosModeldefsCache.getApiUrlFromModel')
+ })
.value('AppConfig', {});
angular.mock.module('test');
@@ -47,10 +53,12 @@
beforeEach(angular.mock.inject((
StoreHelpers: IStoreHelpersService,
+ XosModeldefsCache: IXosModeldefsCache,
_$resource_: ng.resource.IResourceService
) => {
$resource = _$resource_;
resource = $resource('/test');
+ xosModelCache = XosModeldefsCache;
service = StoreHelpers;
}));
@@ -58,13 +66,38 @@
expect(service.updateCollection).toBeDefined();
});
+ describe('the updateCollection method', () => {
- it('should convert a core model name in an URL', () => {
- expect(service.urlFromCoreModel('Slice')).toBe('/core/slices');
- expect(service.urlFromCoreModel('Xos')).toBe('/core/xoses');
- expect(service.urlFromCoreModel('SiteRole')).toBe('/core/siteroles');
- expect(service.urlFromCoreModel('SliceRole')).toBe('/core/sliceroles');
- expect(service.urlFromCoreModel('SlicePrivilege')).toBe('/core/sliceprivileges');
+ beforeEach(() => {
+ subject = new BehaviorSubject([
+ new resource({id: 1, name: 'test'})
+ ]);
+
+ });
+
+ it('should get the correct url for a core model', () => {
+ const mockModelDef = {
+ name: 'Node',
+ app: 'core'
+ };
+
+ xosModelCache.get['and'].returnValue(mockModelDef);
+
+ const event: IWSEvent = {
+ model: 'TestModel',
+ msg: {
+ object: {
+ id: 1,
+ name: 'test'
+ },
+ changed_fields: ['deleted']
+ }
+ };
+
+ service.updateCollection(event, subject);
+ expect(xosModelCache.get).toHaveBeenCalledWith('TestModel');
+ expect(xosModelCache.getApiUrlFromModel).toHaveBeenCalledWith(mockModelDef);
+ });
});
describe('when updating a collection', () => {
diff --git a/src/app/datasources/helpers/store.helpers.ts b/src/app/datasources/helpers/store.helpers.ts
index 83c3b9a..0df37c7 100644
--- a/src/app/datasources/helpers/store.helpers.ts
+++ b/src/app/datasources/helpers/store.helpers.ts
@@ -18,27 +18,26 @@
import {BehaviorSubject} from 'rxjs';
import * as _ from 'lodash';
-import * as pluralize from 'pluralize';
import {IWSEvent} from '../websocket/global';
import {IXosResourceService} from '../rest/model.rest';
+import {IXosModeldefsCache} from './modeldefs.service';
export interface IStoreHelpersService {
- urlFromCoreModel(name: string): string;
updateCollection(event: IWSEvent, subject: BehaviorSubject<any>): BehaviorSubject<any>;
}
export class StoreHelpers implements IStoreHelpersService {
- static $inject = ['ModelRest'];
+ static $inject = [
+ 'ModelRest',
+ 'XosModeldefsCache'
+ ];
constructor (
- private modelRest: IXosResourceService
+ private modelRest: IXosResourceService,
+ private XosModeldefsCache: IXosModeldefsCache
) {
}
- public urlFromCoreModel(name: string): string {
- return `/core/${pluralize(name.toLowerCase())}`;
- }
-
public updateCollection(event: IWSEvent, subject: BehaviorSubject<any>): BehaviorSubject<any> {
const collection: any[] = subject.value;
const index: number = _.findIndex(collection, (i) => {
@@ -49,7 +48,8 @@
const isDeleted: boolean = _.includes(event.msg.changed_fields, 'deleted');
// generate a resource for the model
- const endpoint = this.urlFromCoreModel(event.model);
+ const modelDef = this.XosModeldefsCache.get(event.model); // get the model definition
+ const endpoint = this.XosModeldefsCache.getApiUrlFromModel(modelDef);
const resource = this.modelRest.getResource(endpoint);
const model = new resource(event.msg.object);