Merge "[CORD-1934] Fixing links in table after first page"
diff --git a/conf/app/app.config.production.js b/conf/app/app.config.production.js
index 780a767..e47ea73 100644
--- a/conf/app/app.config.production.js
+++ b/conf/app/app.config.production.js
@@ -19,5 +19,5 @@
angular.module('app')
.constant('AppConfig', {
apiEndpoint: '/xosapi/v1',
- websocketClient: 'http://192.168.46.100'
+ websocketClient: '/'
});
diff --git a/conf/proxy.js b/conf/proxy.js
index 71aca89..e383c27 100644
--- a/conf/proxy.js
+++ b/conf/proxy.js
@@ -20,6 +20,8 @@
const target = process.env.PROXY || '192.168.46.100';
+console.info(`Proxying request to: ${target}`);
+
const proxy = httpProxy.createProxyServer({
target: `http://${target}`
});
diff --git a/src/app/core/services/helpers/config.helpers.spec.ts b/src/app/core/services/helpers/config.helpers.spec.ts
index 7718a44..7120a6b 100644
--- a/src/app/core/services/helpers/config.helpers.spec.ts
+++ b/src/app/core/services/helpers/config.helpers.spec.ts
@@ -25,6 +25,7 @@
import {IXosTableCfg} from '../../table/table';
import {IXosFormInput, IXosFormCfg} from '../../form/form';
import {BehaviorSubject} from 'rxjs';
+import {XosFormHelpers} from '../../form/form-helpers';
let service: IXosConfigHelpersService;
@@ -95,6 +96,7 @@
.value('XosModelStore', {
})
+ .service('XosFormHelpers', XosFormHelpers)
.value('$state', {
get: () => {
return [
@@ -283,9 +285,9 @@
});
describe('the private methods', () => {
- let modelStoreMock, toastr, auth, stateMock;
+ let modelStoreMock, q, toastr, auth, stateMock, XosFormHelpersMock;
- beforeEach(angular.mock.inject((_toastr_, AuthService) => {
+ beforeEach(angular.mock.inject(($q, _toastr_, AuthService, XosFormHelpers) => {
modelStoreMock = {
query: () => {
const subject = new BehaviorSubject([
@@ -297,9 +299,11 @@
};
toastr = _toastr_;
auth = AuthService;
+ XosFormHelpersMock = XosFormHelpers;
stateMock = {
get: ''
};
+ q = $q;
}));
const field: IXosModelDefsField = {
@@ -316,7 +320,7 @@
test: 2
};
it('should add the formatted data to the column definition', () => {
- service = new ConfigHelpers(stateMock, toastr, modelStoreMock);
+ service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
service['populateRelated'](item, item.test, field);
expect(item['test-formatted']).toBe('second');
});
@@ -332,7 +336,7 @@
};
it('should add the available choice to the select', () => {
- service = new ConfigHelpers(stateMock, toastr, modelStoreMock);
+ service = new ConfigHelpers(q, stateMock, toastr, modelStoreMock, XosFormHelpersMock);
service['populateSelectField'](field, input);
expect(input.options).toEqual([
{id: 1, label: 'test'},
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index 2241fd1..16958de 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -23,6 +23,7 @@
import {IXosFormCfg, IXosFormInput, IXosFormInputValidator, IXosFormInputOptions} from '../../form/form';
import {IXosModelStoreService} from '../../../datasources/stores/model.store';
import {IXosState} from '../runtime-states';
+import {IXosFormHelpersService} from '../../form/form-helpers';
export interface IXosModelDefsFieldValidators {
name: string;
@@ -61,9 +62,12 @@
export class ConfigHelpers implements IXosConfigHelpersService {
static $inject = [
+ '$q',
'$state',
'toastr',
- 'XosModelStore'];
+ 'XosModelStore',
+ 'XosFormHelpers'
+ ];
public excluded_fields = [
'created',
@@ -96,9 +100,11 @@
]);
constructor(
+ private $q: ng.IQService,
private $state: ng.ui.IStateService,
private toastr: ng.toastr.IToastrService,
- private XosModelStore: IXosModelStoreService
+ private XosModelStore: IXosModelStoreService,
+ private XosFormHelpers: IXosFormHelpersService
) {
pluralize.addIrregularRule('xos', 'xoses');
pluralize.addPluralRule(/slice$/i, 'slices');
@@ -299,7 +305,7 @@
};
formCfg.actions[0].cb = (item, form: angular.IFormController) => {
-
+ const d = this.$q.defer();
if (!form.$valid) {
formCfg.feedback = {
show: true,
@@ -318,9 +324,15 @@
// remove field added by xosTable
_.forEach(Object.keys(item), prop => {
- if (prop.indexOf('-formatted') > -1) {
+ // FIXME what _ptr fields comes from??
+ if (prop.indexOf('-formatted') > -1 || prop.indexOf('_ptr') > -1) {
delete item[prop];
}
+
+ // convert dates back to UnixTime
+ if (this.XosFormHelpers._getFieldFormat(item[prop]) === 'date') {
+ item[prop] = new Date(item[prop]).getTime() / 1000;
+ }
});
const itemName = (angular.isUndefined(itemCopy.name)) ? model.name : itemCopy.name;
@@ -334,6 +346,7 @@
closeBtn: true
};
this.toastr.success(`${itemName} successfully saved`);
+ d.resolve(res);
})
.catch(err => {
formCfg.feedback = {
@@ -343,7 +356,10 @@
closeBtn: true
};
this.toastr.error(err.specific_error || '', `Error while saving ${itemName}: ${err.error}`);
+ d.reject(err);
});
+
+ return d.promise;
};
return formCfg;
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);
diff --git a/src/app/datasources/index.ts b/src/app/datasources/index.ts
index e5adcab..d5869b5 100644
--- a/src/app/datasources/index.ts
+++ b/src/app/datasources/index.ts
@@ -26,6 +26,7 @@
import {xosCore} from '../core/index';
import {SearchService} from './helpers/search.service';
import {XosModelDiscovererService} from './helpers/model-discoverer.service';
+import {XosModeldefsCache} from './helpers/modeldefs.service';
export const xosDataSources = 'xosDataSources';
@@ -34,6 +35,7 @@
.service('ModelRest', ModelRest)
.service('AuthService', AuthService)
.service('WebSocket', WebSocketEvent)
+ .service('XosModeldefsCache', XosModeldefsCache)
.service('StoreHelpers', StoreHelpers)
.service('SynchronizerStore', SynchronizerStore)
.service('XosModelStore', XosModelStore)
diff --git a/src/app/datasources/stores/model.store.spec.ts b/src/app/datasources/stores/model.store.spec.ts
index 4e94495..09dc3f9 100644
--- a/src/app/datasources/stores/model.store.spec.ts
+++ b/src/app/datasources/stores/model.store.spec.ts
@@ -27,11 +27,13 @@
import {ConfigHelpers} from '../../core/services/helpers/config.helpers';
import {AuthService} from '../rest/auth.rest';
import {XosDebouncer} from '../../core/services/helpers/debounce.helper';
+import {IXosModeldefsCache} from '../helpers/modeldefs.service';
let service: IXosModelStoreService;
let httpBackend: ng.IHttpBackendService;
let $scope;
let WebSocket;
+let XosModeldefsCache;
class MockWs {
private _list;
@@ -69,6 +71,10 @@
.service('ConfigHelpers', ConfigHelpers) // TODO mock
.service('AuthService', AuthService)
.constant('AppConfig', MockAppCfg)
+ .value('XosModeldefsCache', {
+ get: jasmine.createSpy('XosModeldefsCache.get').and.returnValue({}),
+ getApiUrlFromModel: jasmine.createSpy('XosModeldefsCache.getApiUrlFromModel')
+ })
.service('XosDebouncer', XosDebouncer);
angular.mock.module('ModelStore');
@@ -78,12 +84,14 @@
XosModelStore: IXosModelStoreService,
$httpBackend: ng.IHttpBackendService,
_$rootScope_: ng.IRootScopeService,
- _WebSocket_: any
+ _WebSocket_: any,
+ _XosModeldefsCache_: IXosModeldefsCache
) => {
service = XosModelStore;
httpBackend = $httpBackend;
$scope = _$rootScope_;
WebSocket = _WebSocket_;
+ XosModeldefsCache = _XosModeldefsCache_;
// ModelRest will call the backend
httpBackend.whenGET(`${MockAppCfg.apiEndpoint}/core/samples`)
@@ -96,6 +104,7 @@
});
it('the first event should be the resource response', (done) => {
+ XosModeldefsCache.getApiUrlFromModel.and.returnValue(`/core/samples`);
let event = 0;
service.query('sample')
.subscribe(collection => {
@@ -114,6 +123,7 @@
describe('the GET method', () => {
it('should return an observable containing a single model', (done) => {
let event = 0;
+ XosModeldefsCache.getApiUrlFromModel.and.returnValue(`/core/samples`);
service.get('sample', queryData[1].id)
.subscribe((model) => {
event++;
@@ -177,6 +187,8 @@
it('should create different Subject', (done) => {
let fevent = 0;
let sevent = 0;
+ XosModeldefsCache.get.and.callFake(v => v);
+ XosModeldefsCache.getApiUrlFromModel.and.callFake(v => `/core/${v}s`);
service.query('first')
.subscribe(first => {
fevent++;
diff --git a/src/app/datasources/stores/model.store.ts b/src/app/datasources/stores/model.store.ts
index 7735ff0..10c4d2e 100644
--- a/src/app/datasources/stores/model.store.ts
+++ b/src/app/datasources/stores/model.store.ts
@@ -23,6 +23,7 @@
import {IXosResourceService} from '../rest/model.rest';
import {IStoreHelpersService} from '../helpers/store.helpers';
import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
+import {IXosModeldefsCache} from '../helpers/modeldefs.service';
export interface IXosModelStoreService {
query(model: string, apiUrl?: string): Observable<any>;
@@ -31,7 +32,14 @@
}
export class XosModelStore implements IXosModelStoreService {
- static $inject = ['$log', 'WebSocket', 'StoreHelpers', 'ModelRest', 'XosDebouncer'];
+ static $inject = [
+ '$log',
+ 'WebSocket',
+ 'StoreHelpers',
+ 'ModelRest',
+ 'XosDebouncer',
+ 'XosModeldefsCache'
+ ];
private _collections: any; // NOTE contains a map of {model: BehaviourSubject}
private efficientNext: any; // NOTE debounce next
constructor(
@@ -39,7 +47,8 @@
private webSocket: IWSEventService,
private storeHelpers: IStoreHelpersService,
private ModelRest: IXosResourceService,
- private XosDebouncer: IXosDebouncer
+ private XosDebouncer: IXosDebouncer,
+ private XosModeldefsCache: IXosModeldefsCache
) {
this._collections = {};
this.efficientNext = this.XosDebouncer.debounce(this.next, 500, this, false);
@@ -59,6 +68,7 @@
this.efficientNext(this._collections[modelName]);
}
+ // NOTE do we need to subscriber every time we query?
this.webSocket.list()
.filter((e: IWSEvent) => e.model === modelName)
.subscribe(
@@ -128,8 +138,7 @@
private loadInitialData(model: string, apiUrl?: string) {
// TODO provide always the apiUrl together with the query() params
if (!angular.isDefined(apiUrl)) {
- // NOTE check what is the correct pattern to pluralize this
- apiUrl = this.storeHelpers.urlFromCoreModel(model);
+ apiUrl = this.XosModeldefsCache.getApiUrlFromModel(this.XosModeldefsCache.get(model));
}
this.ModelRest.getResource(apiUrl).query().$promise
.then(
diff --git a/src/app/datasources/websocket/global.ts b/src/app/datasources/websocket/global.ts
index 32f6f4a..e86282a 100644
--- a/src/app/datasources/websocket/global.ts
+++ b/src/app/datasources/websocket/global.ts
@@ -57,7 +57,6 @@
if (data.msg.changed_fields.length === 0 || _.intersection(data.msg.changed_fields, ignoredFields).length === data.msg.changed_fields.length) {
// NOTE means that the only updated fields does not change anything in the UI, so don't send events around
- this.$log.debug(`[WebSocket] Ignoring Event for: ${data.model} [${data.msg.pk}]`);
return;
}
diff --git a/src/app/service-graph/components/fine-grained/fine-grained.component.ts b/src/app/service-graph/components/fine-grained/fine-grained.component.ts
index 3b51a02..7d21131 100644
--- a/src/app/service-graph/components/fine-grained/fine-grained.component.ts
+++ b/src/app/service-graph/components/fine-grained/fine-grained.component.ts
@@ -24,11 +24,11 @@
import {XosServiceGraphConfig as config} from '../../graph.config';
import {IXosDebouncer} from '../../../core/services/helpers/debounce.helper';
import {IXosServiceGraph, IXosServiceGraphLink, IXosServiceGraphNode} from '../../interfaces';
-import {IXosModelDiscovererService} from '../../../datasources/helpers/model-discoverer.service';
import {IXosSidePanelService} from '../../../core/side-panel/side-panel.service';
import {IXosGraphHelpers} from '../../services/d3-helpers/graph.helpers';
import {IXosServiceGraphExtender, IXosServiceGraphReducer} from '../../services/graph.extender';
import {IXosServiceInstanceGraphStore} from '../../services/service-instance.graph.store';
+import {IXosModeldefsCache} from '../../../datasources/helpers/modeldefs.service';
class XosFineGrainedTenancyGraphCtrl {
static $inject = [
@@ -59,7 +59,7 @@
private $log: ng.ILogService,
private XosServiceInstanceGraphStore: IXosServiceInstanceGraphStore,
private XosDebouncer: IXosDebouncer,
- private XosModelDiscoverer: IXosModelDiscovererService,
+ private XosModeldefsCache: IXosModeldefsCache,
private XosSidePanel: IXosSidePanelService,
private XosGraphHelpers: IXosGraphHelpers,
private XosServiceGraphExtender: IXosServiceGraphExtender
@@ -352,7 +352,7 @@
}
selectedModel = n.id;
const modelName = n.model['class_names'].split(',')[0];
- const formConfig = this.XosModelDiscoverer.get(modelName).formCfg;
+ const formConfig = this.XosModeldefsCache.get(modelName).formCfg;
const model = angular.copy(n.model);
delete model.d3Id;
this.XosSidePanel.injectComponent('xosForm', {config: formConfig, ngModel: model});
diff --git a/src/app/views/crud/crud.relations.service.spec.ts b/src/app/views/crud/crud.relations.service.spec.ts
index 771c9a8..768c039 100644
--- a/src/app/views/crud/crud.relations.service.spec.ts
+++ b/src/app/views/crud/crud.relations.service.spec.ts
@@ -22,13 +22,14 @@
} from './crud.relations.service';
import {BehaviorSubject} from 'rxjs';
import {ConfigHelpers} from '../../core/services/helpers/config.helpers';
+import {XosFormHelpers} from '../../core/form/form-helpers';
const XosModelStoreMock = {
get: null,
query: null
};
-const XosModelDiscovererMock = {
+const XosModeldefsCacheMock = {
get: null
};
@@ -40,8 +41,9 @@
.module('test', ['ui.router', 'toastr'])
.service('XosCrudRelation', XosCrudRelationService)
.value('XosModelStore', XosModelStoreMock)
- .value('XosModelDiscoverer', XosModelDiscovererMock)
- .service('ConfigHelpers', ConfigHelpers);
+ .value('XosModeldefsCache', XosModeldefsCacheMock)
+ .service('ConfigHelpers', ConfigHelpers)
+ .service('XosFormHelpers', XosFormHelpers);
angular.mock.module('test');
});
@@ -133,7 +135,7 @@
subject.next(resModel);
return subject.asObservable();
});
- spyOn(XosModelDiscovererMock, 'get').and.returnValue({formCfg: resFormCfg});
+ spyOn(XosModeldefsCacheMock, 'get').and.returnValue({formCfg: resFormCfg});
service.getModel(relation, '5')
.then((res: IXosCrudRelationFormTabData) => {
@@ -164,7 +166,7 @@
const subject = new BehaviorSubject(resModels);
return subject.asObservable();
});
- spyOn(XosModelDiscovererMock, 'get').and.returnValue({tableCfg: resTableCfg});
+ spyOn(XosModeldefsCacheMock, 'get').and.returnValue({tableCfg: resTableCfg});
service.getModels(relation, 5)
.then((res: IXosCrudRelationTableTabData) => {
@@ -196,7 +198,7 @@
const subject = new BehaviorSubject(resModels);
return subject.asObservable();
});
- spyOn(XosModelDiscovererMock, 'get').and.returnValue({tableCfg: resTableCfg});
+ spyOn(XosModeldefsCacheMock, 'get').and.returnValue({tableCfg: resTableCfg});
service.getModels(relation, 5)
.then((res: IXosCrudRelationTableTabData) => {
diff --git a/src/app/views/crud/crud.relations.service.ts b/src/app/views/crud/crud.relations.service.ts
index 092dda8..9dd3656 100644
--- a/src/app/views/crud/crud.relations.service.ts
+++ b/src/app/views/crud/crud.relations.service.ts
@@ -18,12 +18,12 @@
import {IXosModelRelation} from './crud';
import {IXosModelStoreService} from '../../datasources/stores/model.store';
-import {IXosModelDiscovererService} from '../../datasources/helpers/model-discoverer.service';
import * as _ from 'lodash';
import {IXosFormCfg} from '../../core/form/form';
import {IXosTableCfg} from '../../core/table/table';
import {IXosConfigHelpersService} from '../../core/services/helpers/config.helpers';
import {Subscription} from 'rxjs';
+import {IXosModeldefsCache} from '../../datasources/helpers/modeldefs.service';
interface IXosCrudRelationBaseTabData {
model: any;
@@ -51,16 +51,16 @@
'$log',
'$q',
'XosModelStore',
- 'XosModelDiscoverer',
- 'ConfigHelpers'
+ 'ConfigHelpers',
+ 'XosModeldefsCache'
];
constructor (
private $log: ng.ILogService,
private $q: ng.IQService,
private XosModelStore: IXosModelStoreService,
- private XosModelDiscovererService: IXosModelDiscovererService,
- private ConfigHelpers: IXosConfigHelpersService
+ private ConfigHelpers: IXosConfigHelpersService,
+ private XosModeldefsCache: IXosModeldefsCache
) {}
public getModel (r: IXosModelRelation, id: string | number): Promise<IXosCrudRelationFormTabData> {
@@ -72,7 +72,7 @@
const data: IXosCrudRelationFormTabData = {
model: item,
- formConfig: this.XosModelDiscovererService.get(r.model).formCfg,
+ formConfig: this.XosModeldefsCache.get(r.model).formCfg,
class: angular ? 'full' : 'empty'
};
@@ -96,7 +96,7 @@
match[`${r.on_field.toLowerCase()}_id`] = source_id;
const filtered = _.filter(items, match);
// removing search bar from table
- const tableCfg = this.XosModelDiscovererService.get(r.model).tableCfg;
+ const tableCfg = this.XosModeldefsCache.get(r.model).tableCfg;
tableCfg.filter = null;
const data: IXosCrudRelationTableTabData = {
diff --git a/src/app/views/crud/crud.ts b/src/app/views/crud/crud.ts
index b4434c8..0fe9407 100644
--- a/src/app/views/crud/crud.ts
+++ b/src/app/views/crud/crud.ts
@@ -28,6 +28,7 @@
import {IXosDebugService, IXosDebugStatus} from '../../core/debug/debug.service';
import {IXosKeyboardShortcutService} from '../../core/services/keyboard-shortcut';
import {Subscription} from 'rxjs';
+import {IXosModeldefsCache} from '../../datasources/helpers/modeldefs.service';
export interface IXosModelRelation {
model: string;
@@ -48,7 +49,8 @@
'XosModelDiscoverer',
'XosCrudRelation',
'XosDebug',
- 'XosKeyboardShortcut'
+ 'XosKeyboardShortcut',
+ 'XosModeldefsCache'
];
// bindings
@@ -88,12 +90,13 @@
private XosModelDiscovererService: IXosModelDiscovererService,
private XosCrudRelation: IXosCrudRelationService,
private XosDebug: IXosDebugService,
- private XosKeyboardShortcut: IXosKeyboardShortcutService
+ private XosKeyboardShortcut: IXosKeyboardShortcutService,
+ private XosModeldefsCache: IXosModeldefsCache
) {
this.$log.info('[XosCrud] Setup', $state.current.data);
this.data = this.$state.current.data;
- this.modelDef = this.XosModelDiscovererService.get(this.data.model);
+ this.modelDef = this.XosModeldefsCache.get(this.data.model);
this.modelName = this.modelDef.verbose_name ? this.modelDef.verbose_name : this.modelDef.name;
this.pluralTitle = this.ConfigHelpers.pluralize(this.modelName);
this.singularTitle = this.ConfigHelpers.pluralize(this.modelName, 1);
@@ -106,6 +109,18 @@
this.tableCfg = this.modelDef.tableCfg;
this.formCfg = this.modelDef.formCfg;
+ // attach a redirect to the $save method
+ const originalSave = this.formCfg.actions[0].cb;
+ this.formCfg.actions[0].cb = (item, form: angular.IFormController) => {
+ originalSave(item, form)
+ .then(res => {
+ this.$state.go(this.$state.current, {id: res.id});
+ })
+ .catch(err => {
+ this.$log.error(`[XosCrud] Error while saving:`, item, err);
+ });
+ };
+
this.debugTab = this.XosDebug.status.modelsTab;
this.$scope.$on('xos.debug.status', (e, status: IXosDebugStatus) => {
this.debugTab = status.modelsTab;
@@ -119,7 +134,7 @@
// if it is the create page
if ($stateParams['id'] === 'add') {
// generate a resource for an empty model
- const endpoint = this.XosModelDiscovererService.getApiUrlFromModel(this.XosModelDiscovererService.get(this.data.model));
+ const endpoint = this.XosModelDiscovererService.getApiUrlFromModel(this.XosModeldefsCache.get(this.data.model));
const resource = this.ModelRest.getResource(endpoint);
this.model = new resource({});
}