[CORD-873] CRUD for Core and Service model from Chameleon
Change-Id: I45c533feba6720b82de3681d862773047e7fd6f8
diff --git a/src/app/core/services/helpers/config.helpers.spec.ts b/src/app/core/services/helpers/config.helpers.spec.ts
index 3e90d33..ed41228 100644
--- a/src/app/core/services/helpers/config.helpers.spec.ts
+++ b/src/app/core/services/helpers/config.helpers.spec.ts
@@ -3,47 +3,60 @@
import 'angular-ui-router';
import {IXosConfigHelpersService, ConfigHelpers, IXosModelDefsField} from './config.helpers';
-import {IModeldef} from '../../../datasources/rest/modeldefs.rest';
+import {IXosModeldef} from '../../../datasources/rest/modeldefs.rest';
import {IXosTableCfg} from '../../table/table';
-import {IXosFormInput, IXosFormConfig} from '../../form/form';
+import {IXosFormInput, IXosFormCfg} from '../../form/form';
import {BehaviorSubject} from 'rxjs';
let service: IXosConfigHelpersService;
-const model: IModeldef = {
+const model: IXosModeldef = {
name: 'Test',
+ app: 'test',
fields: [
{
type: 'number',
name: 'id',
- validators: {}
+ validators: []
},
{
type: 'string',
name: 'name',
- validators: {
- required: true
- }
+ validators: [
+ {
+ bool_value: true,
+ name: 'required'
+ }
+ ]
},
{
type: 'string',
name: 'something',
- validators: {
- maxlength: 30
- }
+ validators: [
+ {
+ int_value: 30,
+ name: 'maxlength'
+ }
+ ]
},
{
type: 'number',
name: 'else',
- validators: {
- min: 20,
- max: 40
- }
+ validators: [
+ {
+ int_value: 20,
+ name: 'min'
+ },
+ {
+ int_value: 40,
+ name: 'max'
+ }
+ ]
},
{
type: 'date',
name: 'updated',
- validators: {}
+ validators: []
},
]
};
@@ -59,7 +72,7 @@
return {id: 1};
}
})
- .value('ModelStore', {
+ .value('XosModelStore', {
})
.value('$state', {
@@ -131,11 +144,6 @@
});
describe('the navigation methods', () => {
- describe('urlFromCoreModels', () => {
- it('should return the URL for a given model', () => {
- expect(service.urlFromCoreModel('Test')).toBe('/core/tests');
- });
- });
describe('stateFromCoreModels', () => {
let state: ng.ui.IStateService;
@@ -156,7 +164,7 @@
describe('the modelFieldsToColumnsCfg method', () => {
it('should return an array of columns', () => {
- const cols = service.modelFieldsToColumnsCfg(model.fields, 'testUrl/:id?');
+ const cols = service.modelFieldsToColumnsCfg({fields: model.fields, name: 'testUrl', app: 'test'});
expect(cols[0].label).toBe('Id');
expect(cols[0].prop).toBe('id');
expect(cols[0].link).toBeDefined();
@@ -214,7 +222,7 @@
describe('the modelToFormCfg method', () => {
it('should return a form config', () => {
- const config: IXosFormConfig = service.modelToFormCfg(model);
+ const config: IXosFormCfg = service.modelToFormCfg(model);
expect(config.formName).toBe('TestForm');
expect(config.actions.length).toBe(1);
expect(config.actions[0].label).toBe('Save');
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index 28a16a3..2ac9394 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -1,16 +1,22 @@
import * as _ from 'lodash';
import * as pluralize from 'pluralize';
import {IXosTableColumn, IXosTableCfg} from '../../table/table';
-import {IModeldef} from '../../../datasources/rest/modeldefs.rest';
-import {IXosFormConfig, IXosFormInput} from '../../form/form';
+import {IXosModeldef} from '../../../datasources/rest/modeldefs.rest';
+import {IXosFormCfg, IXosFormInput, IXosFormInputValidator} from '../../form/form';
import {IXosAuthService} from '../../../datasources/rest/auth.rest';
import {IXosModelStoreService} from '../../../datasources/stores/model.store';
-import {IXosState} from '../../../../index';
+import {IXosState} from '../runtime-states';
+
+export interface IXosModelDefsFieldValidators {
+ name: string;
+ bool_value?: boolean;
+ int_value?: number;
+}
export interface IXosModelDefsField {
name: string;
type: string;
- validators?: any;
+ validators?: IXosModelDefsFieldValidators[];
hint?: string;
relation?: {
model: string;
@@ -20,23 +26,26 @@
export interface IXosConfigHelpersService {
excluded_fields: string[];
- modelFieldsToColumnsCfg(fields: IXosModelDefsField[], baseUrl: string): IXosTableColumn[]; // TODO use a proper interface
- modelToTableCfg(model: IModeldef, modelName: string): IXosTableCfg;
+ modelFieldsToColumnsCfg(model: IXosModeldef): IXosTableColumn[];
+ modelToTableCfg(model: IXosModeldef, modelName: string): IXosTableCfg;
modelFieldToInputCfg(fields: IXosModelDefsField[]): IXosFormInput[];
- modelToFormCfg(model: IModeldef): IXosFormConfig;
+ modelToFormCfg(model: IXosModeldef): IXosFormCfg;
pluralize(string: string, quantity?: number, count?: boolean): string;
toLabel(string: string, pluralize?: boolean): string;
toLabels(string: string[], pluralize?: boolean): string[];
- urlFromCoreModel(model: string): string;
stateFromCoreModel(name: string): string;
stateWithParams(name: string, model: any): string;
stateWithParamsForJs(name: string, model: any): any;
}
export class ConfigHelpers implements IXosConfigHelpersService {
- static $inject = ['$state', 'toastr', 'AuthService', 'ModelStore'];
+ static $inject = [
+ '$state',
+ 'toastr',
+ 'AuthService',
+ 'XosModelStore'];
- excluded_fields = [
+ public excluded_fields = [
'created',
'updated',
'enacted',
@@ -59,11 +68,15 @@
private $state: ng.ui.IStateService,
private toastr: ng.toastr.IToastrService,
private AuthService: IXosAuthService,
- private ModelStore: IXosModelStoreService
+ private XosModelStore: IXosModelStoreService
) {
pluralize.addIrregularRule('xos', 'xoses');
pluralize.addPluralRule(/slice$/i, 'slices');
pluralize.addSingularRule(/slice$/i, 'slice');
+ pluralize.addPluralRule(/library$/i, 'librarys');
+ pluralize.addPluralRule(/imagedeployments/i, 'imagedeploymentses');
+ pluralize.addPluralRule(/controllerimages/i, 'controllerimageses');
+
}
public pluralize(string: string, quantity?: number, count?: boolean): string {
@@ -91,9 +104,9 @@
return this.capitalizeFirst(string);
}
- public modelToTableCfg(model: IModeldef, baseUrl: string): IXosTableCfg {
+ public modelToTableCfg(model: IXosModeldef, baseUrl: string): IXosTableCfg {
const cfg = {
- columns: this.modelFieldsToColumnsCfg(model.fields, model.name),
+ columns: this.modelFieldsToColumnsCfg(model),
filter: 'fulltext',
order: {field: 'id', reverse: false},
pagination: {
@@ -125,10 +138,11 @@
return cfg;
}
- public modelFieldsToColumnsCfg(fields: IXosModelDefsField[], modelName: string): IXosTableColumn[] {
-
+ public modelFieldsToColumnsCfg(model: IXosModeldef): IXosTableColumn[] {
+ const fields: IXosModelDefsField[] = model.fields;
+ const modelName: string = model.name;
const columns = _.map(fields, (f) => {
- if (this.excluded_fields.indexOf(f.name) > -1) {
+ if (!angular.isDefined(f) || this.excluded_fields.indexOf(f.name) > -1) {
return;
}
const col: IXosTableColumn = {
@@ -147,7 +161,9 @@
this.populateRelated(item, item[f.name], f);
return item[f.name];
};
- col.link = item => this.stateWithParams(f.relation.model, item);
+ col.link = item => {
+ return this.stateWithParams(f.relation.model, item);
+ };
}
if (f.name === 'backend_status') {
@@ -175,11 +191,6 @@
return columns;
};
- public urlFromCoreModel(name: string): string {
-
- return `/core/${this.pluralize(name.toLowerCase())}`;
- }
-
public stateFromCoreModel(name: string): string {
const state: ng.ui.IState = _.find(this.$state.get(), (s: IXosState) => {
if (s.data) {
@@ -187,7 +198,7 @@
}
return false;
});
- return state.name;
+ return state ? state.name : null;
}
public stateWithParams(name: string, model: any): string {
@@ -204,32 +215,27 @@
public modelFieldToInputCfg(fields: IXosModelDefsField[]): IXosFormInput[] {
return _.map(fields, (f: IXosModelDefsField) => {
- if (f.relation) {
- const input: IXosFormInput = {
- name: f.name,
- label: this.toLabel(f.name),
- type: 'select',
- validators: f.validators,
- hint: f.hint
- };
- this.populateSelectField(f, input);
- return input;
- }
-
- return {
+ const input: IXosFormInput = {
name: f.name,
label: this.toLabel(f.name),
type: f.type,
- validators: f.validators
+ validators: this.formatValidators(f.validators),
+ hint: f.hint
};
+ if (f.relation) {
+ input.type = 'select';
+ this.populateSelectField(f, input);
+ return input;
+ }
+ return input;
})
.filter(f => this.excluded_fields.indexOf(f.name) === -1);
}
- public modelToFormCfg(model: IModeldef): IXosFormConfig {
- const formCfg: IXosFormConfig = {
+ public modelToFormCfg(model: IXosModeldef): IXosFormCfg {
+ const formCfg: IXosFormCfg = {
formName: `${model.name}Form`,
- exclude: ['backend_status', 'creator'],
+ exclude: ['backend_status', 'creator', 'id'],
actions: [{
label: 'Save',
class: 'success',
@@ -258,12 +264,19 @@
delete item.networks;
// adding userId as creator
- item.creator = this.AuthService.getUser().id;
+ // item.creator = this.AuthService.getUser().id;
+
+ // remove field added by xosTable
+ _.forEach(Object.keys(item), prop => {
+ if (prop.indexOf('-formatted') > -1) {
+ delete item[prop];
+ }
+ });
item.$save()
.then((res) => {
if (res.status === 403 || res.status === 405 || res.status === 500) {
- // TODO understand why 405 does not go directly in catch (it may be realted to ng-rest-gw)
+ // TODO understand why 405 does not go directly in catch (it may be related to ng-rest-gw)
throw new Error();
}
formCfg.feedback = {
@@ -283,6 +296,15 @@
return formCfg;
}
+ private formatValidators(validators: IXosModelDefsFieldValidators[]): IXosFormInputValidator {
+ // convert validators as expressed from modelDefs,
+ // to the object required by xosForm
+ return _.reduce(validators, (formValidators: IXosFormInputValidator, v: IXosModelDefsFieldValidators) => {
+ formValidators[v.name] = v.bool_value ? v.bool_value : v.int_value;
+ return formValidators;
+ }, {});
+ }
+
private fromCamelCase(string: string): string {
return string.split(/(?=[A-Z])/).map(w => w.toLowerCase()).join(' ');
}
@@ -304,12 +326,20 @@
if (!fk || angular.isUndefined(fk) || fk === null) {
return;
}
- this.ModelStore.query(field.relation.model)
+ this.XosModelStore.query(field.relation.model)
.subscribe(res => {
if (angular.isDefined(res) && angular.isDefined(fk)) {
let ri = _.find(res, {id: fk});
if (angular.isDefined(ri)) {
- item[`${field.name}-formatted`] = angular.isDefined(ri.name) ? ri.name : ri.humanReadableName;
+ if (angular.isDefined(ri.name)) {
+ item[`${field.name}-formatted`] = ri.name;
+ }
+ else if (angular.isDefined(ri.humanReadableName)) {
+ item[`${field.name}-formatted`] = ri.humanReadableName;
+ }
+ else {
+ item[`${field.name}-formatted`] = ri.id;
+ }
}
}
});
@@ -317,10 +347,14 @@
// augment a select field with related model informations
private populateSelectField(field: IXosModelDefsField, input: IXosFormInput): void {
- this.ModelStore.query(field.relation.model)
+ this.XosModelStore.query(field.relation.model)
.subscribe(res => {
input.options = _.map(res, item => {
- return {id: item.id, label: item.humanReadableName ? item.humanReadableName : item.name};
+ let opt = {id: item.id, label: item.humanReadableName ? item.humanReadableName : item.name};
+ if (!angular.isDefined(item.humanReadableName) && !angular.isDefined(item.name)) {
+ opt.label = item.id;
+ }
+ return opt;
});
});
}
diff --git a/src/app/core/services/helpers/model-setup.helpers.ts b/src/app/core/services/helpers/model-setup.helpers.ts
deleted file mode 100644
index e79899d..0000000
--- a/src/app/core/services/helpers/model-setup.helpers.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import {ModeldefsService, IModeldef} from '../../../datasources/rest/modeldefs.rest';
-import {IXosConfigHelpersService} from './config.helpers';
-import {IRuntimeStatesService} from '../runtime-states';
-import {NavigationService} from '../navigation';
-import {IXosState} from '../../../../index';
-import * as _ from 'lodash';
-import IPromise = angular.IPromise;
-
-export interface IXosModelSetupService {
- setup(): IPromise<null>;
-}
-
-export class ModelSetup {
- static $inject = ['$rootScope', '$q', 'ModelDefs', 'ConfigHelpers', 'RuntimeStates', 'NavigationService'];
-
- constructor(
- private $rootScope: ng.IScope,
- private $q: ng.IQService,
- private ModelDefs: ModeldefsService,
- private ConfigHelpers: IXosConfigHelpersService,
- private RuntimeStates: IRuntimeStatesService,
- private NavigationService: NavigationService
- ) {
-
- }
-
- public setup(): IPromise<null> {
- const d = this.$q.defer();
- this.ModelDefs.get()
- .then((models: IModeldef[]) => {
- _.forEach(models, (m: IModeldef) => {
- const stateUrl = `/${this.ConfigHelpers.pluralize(m.name.toLowerCase())}/:id?`;
- const stateName = `xos.core.${this.ConfigHelpers.pluralize(m.name.toLowerCase())}`;
- const state: IXosState = {
- parent: 'core',
- url: stateUrl,
- component: 'xosCrud',
- params: {
- id: null
- },
- data: {
- model: m.name,
- related: m.relations,
- xosTableCfg: this.ConfigHelpers.modelToTableCfg(m, stateUrl),
- xosFormCfg: this.ConfigHelpers.modelToFormCfg(m)
- }
- };
-
- this.RuntimeStates.addState(stateName, state);
- this.NavigationService.add({
- label: this.ConfigHelpers.pluralize(m.name),
- state: stateName,
- parent: 'xos.core'
- });
- });
-
- d.resolve();
- })
- .catch(d.reject);
-
- return d.promise;
- }
-}
diff --git a/src/app/core/services/navigation.spec.ts b/src/app/core/services/navigation.spec.ts
index fbfb91c..82a235e 100644
--- a/src/app/core/services/navigation.spec.ts
+++ b/src/app/core/services/navigation.spec.ts
@@ -35,10 +35,10 @@
});
beforeEach(angular.mock.inject((
- NavigationService: IXosNavigationService,
+ XosNavigationService: IXosNavigationService,
_$log_: ng.ILogService
) => {
- service = NavigationService;
+ service = XosNavigationService;
$log = _$log_;
spyOn($log, 'warn');
defaultRoutes = [
diff --git a/src/app/core/services/navigation.ts b/src/app/core/services/navigation.ts
index e35c9f8..9869b5a 100644
--- a/src/app/core/services/navigation.ts
+++ b/src/app/core/services/navigation.ts
@@ -17,7 +17,9 @@
add(route: IXosNavigationRoute): void;
}
-export class NavigationService {
+// TODO support 3rd level to group service model under "Services"
+
+export class IXosNavigationService {
static $inject = ['$log', 'StyleConfig'];
private routes: IXosNavigationRoute[];
@@ -34,6 +36,10 @@
label: 'Core',
state: 'xos.core'
},
+ // {
+ // label: 'Service',
+ // state: 'xos.services'
+ // },
];
// adding configuration defined routes
// this.routes = StyleConfig.routes.concat(defaultRoutes).reverse();
@@ -64,16 +70,20 @@
return;
}
-
if (angular.isDefined(route.parent)) {
// route parent should be a state for now
const parentRoute = _.find(this.routes, {state: route.parent});
-
- if (angular.isArray(parentRoute.children)) {
- parentRoute.children.push(route);
+ if (angular.isDefined(parentRoute)) {
+ if (angular.isArray(parentRoute.children)) {
+ parentRoute.children.push(route);
+ }
+ else {
+ parentRoute.children = [route];
+ }
}
else {
- parentRoute.children = [route];
+ this.$log.warn(`[XosNavigation] Parent State (${route.parent}) for state: ${route.state} does not exists`);
+ return;
}
}
else {
diff --git a/src/app/core/services/runtime-states.spec.ts b/src/app/core/services/runtime-states.spec.ts
index 5dd44db..8b9015d 100644
--- a/src/app/core/services/runtime-states.spec.ts
+++ b/src/app/core/services/runtime-states.spec.ts
@@ -2,19 +2,19 @@
import 'angular-mocks';
import 'angular-ui-router';
import {xosCore} from '../index';
-import {IRuntimeStatesService} from './runtime-states';
+import {IXosRuntimeStatesService} from './runtime-states';
-let service: IRuntimeStatesService, $state: ng.ui.IStateService;
+let service: IXosRuntimeStatesService, $state: ng.ui.IStateService;
describe('The Navigation service', () => {
beforeEach(angular.mock.module(xosCore));
beforeEach(angular.mock.inject((
- RuntimeStates: IRuntimeStatesService,
+ XosRuntimeStates: IXosRuntimeStatesService,
_$state_: ng.ui.IStateService
) => {
- service = RuntimeStates;
+ service = XosRuntimeStates;
$state = _$state_;
}));
diff --git a/src/app/core/services/runtime-states.ts b/src/app/core/services/runtime-states.ts
index 652b554..1f495ba 100644
--- a/src/app/core/services/runtime-states.ts
+++ b/src/app/core/services/runtime-states.ts
@@ -1,9 +1,14 @@
-import {IXosState} from '../../../index';
-export interface IRuntimeStatesService {
+export interface IXosState extends angular.ui.IState {
+ data: {
+ model: string
+ };
+};
+
+export interface IXosRuntimeStatesService {
addState(name: string, state: ng.ui.IState): void;
}
-export function RuntimeStates($stateProvider: ng.ui.IStateProvider): ng.IServiceProvider {
+export function XosRuntimeStates($stateProvider: ng.ui.IStateProvider): ng.IServiceProvider {
this.$get = function($state: ng.ui.IStateService) {
return {
addState: function(name: string, state: IXosState) {