blob: 10788533a340ab1ccbfc5639a59bec783d2bdead [file] [log] [blame]
Matteo Scandolo1aee1982017-02-17 08:33:23 -08001// TODO test me hard!!!
2
3import * as _ from 'lodash';
4import {IXosModeldefsService, IXosModeldef, IXosModelDefsField, IXosModelDefsRelation} from '../rest/modeldefs.rest';
5import {IXosTableCfg} from '../../core/table/table';
6import {IXosFormCfg} from '../../core/form/form';
7import {IXosNavigationService} from '../../core/services/navigation';
8import {IXosConfigHelpersService} from '../../core/services/helpers/config.helpers';
9import {IXosRuntimeStatesService, IXosState} from '../../core/services/runtime-states';
10import {IXosModelStoreService} from '../stores/model.store';
11
12export interface IXosModel {
13 name: string; // the model name
14 app: string; // the service to wich it belong
15 fields: IXosModelDefsField[];
16 relations?: IXosModelDefsRelation[];
17 backendUrl?: string; // the api endpoint
18 clientUrl?: string; // the view url
19 tableCfg?: IXosTableCfg;
20 formCfg?: IXosFormCfg;
21}
22
23// Service
24export interface IXosModelDiscovererService {
25 discover(): ng.IPromise<boolean>;
26 get(modelName: string): IXosModel;
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070027 getApiUrlFromModel(model: IXosModel): string;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080028}
29
30export class XosModelDiscovererService implements IXosModelDiscovererService {
31 static $inject = [
32 '$log',
33 '$q',
34 'XosModelDefs',
35 'ConfigHelpers',
36 'XosRuntimeStates',
37 'XosNavigationService',
Matteo Scandolo042ea632017-03-01 19:02:34 -080038 'XosModelStore',
39 'ngProgressFactory'
Matteo Scandolo1aee1982017-02-17 08:33:23 -080040 ];
41 private xosModels: IXosModel[] = []; // list of augmented model definitions;
42 private xosServices: string[] = []; // list of loaded services
Matteo Scandolo042ea632017-03-01 19:02:34 -080043 private progressBar;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080044
45 constructor (
46 private $log: ng.ILogService,
47 private $q: ng.IQService,
48 private XosModelDefs: IXosModeldefsService,
49 private ConfigHelpers: IXosConfigHelpersService,
50 private XosRuntimeStates: IXosRuntimeStatesService,
51 private XosNavigationService: IXosNavigationService,
Matteo Scandolo042ea632017-03-01 19:02:34 -080052 private XosModelStore: IXosModelStoreService,
53 private ngProgressFactory: any // check for type defs
Matteo Scandolo1aee1982017-02-17 08:33:23 -080054 ) {
Matteo Scandolo042ea632017-03-01 19:02:34 -080055 this.progressBar = this.ngProgressFactory.createInstance();
56 this.progressBar.setColor('#f6a821');
Matteo Scandolo1aee1982017-02-17 08:33:23 -080057 }
58
59 public get(modelName: string): IXosModel|null {
60 return _.find(this.xosModels, m => m.name === modelName);
61 }
62
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070063 public getApiUrlFromModel(model: IXosModel): string {
64 if (model.app === 'core') {
65 return `/core/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
66 }
67 else {
68 const serviceName = this.serviceNameFromAppName(model.app);
69 return `/${serviceName}/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
70 }
71 }
72
Matteo Scandolo1aee1982017-02-17 08:33:23 -080073 public discover() {
74 const d = this.$q.defer();
Matteo Scandolo042ea632017-03-01 19:02:34 -080075 this.progressBar.start();
Matteo Scandolo1aee1982017-02-17 08:33:23 -080076 this.XosModelDefs.get()
77 .then((modelsDef: IXosModeldef[]) => {
78
79 const pArray = [];
80 _.forEach(modelsDef, (model: IXosModeldef) => {
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -080081 this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
Matteo Scandolo1aee1982017-02-17 08:33:23 -080082 let p = this.cacheModelEntries(model)
83 .then(model => {
84 return this.addState(model);
85 })
86 .then(model => {
87 return this.addNavItem(model);
88 })
89 .then(model => {
90 return this.getTableCfg(model);
91 })
92 .then(model => {
93 return this.getFormCfg(model);
94 })
95 .then(model => {
96 return this.storeModel(model);
97 })
98 .then(model => {
99 this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800100 return this.$q.resolve('true');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800101 })
102 .catch(err => {
103 this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800104 return this.$q.resolve('false');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800105 });
106 pArray.push(p);
107 });
108 this.$q.all(pArray)
Matteo Scandolo042ea632017-03-01 19:02:34 -0800109 .then((res) => {
110 // the Model Loader promise won't ever be reject, in case it will be resolve with value false,
111 // that's because we want to wait anyway for all the models to be loaded
112 if (res.indexOf('false') > -1) {
113 d.resolve(false);
114 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800115 d.resolve(true);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800116 })
117 .catch(() => {
118 d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800119 })
120 .finally(() => {
121 this.progressBar.complete();
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800122 });
123 });
124 return d.promise;
125 }
126
127 private serviceNameFromAppName(appName: string): string {
128 return appName.replace('services.', '');
129 }
130
131 private stateNameFromModel(model: IXosModel): string {
132 return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
133 }
134
135 private getParentStateFromModel(model: IXosModel): string {
136 let parentState: string;
137 if (model.app === 'core') {
138 parentState = 'xos.core';
139 }
140 else {
141 const serviceName = this.addService(model);
142 parentState = `xos.${serviceName}`;
143 }
144 return parentState;
145 }
146
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800147 // add a service state and navigation item if it is not already there
148 private addService(model: IXosModel): string {
149 const serviceName: string = this.serviceNameFromAppName(model.app);
150 if (!_.find(this.xosServices, n => n === serviceName)) {
151 const serviceState = {
152 url: serviceName,
153 parent: 'xos',
154 abstract: true,
155 template: '<div ui-view></div>'
156 };
157 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
158
159 this.XosNavigationService.add({
160 label: this.ConfigHelpers.toLabel(serviceName),
161 state: `xos.${serviceName}`,
162 });
163 this.xosServices.push(serviceName);
164 }
165 return serviceName;
166 }
167
168 private addState(model: IXosModel): ng.IPromise<IXosModel> {
169 const d = this.$q.defer();
170 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
171 const state: IXosState = {
172 parent: this.getParentStateFromModel(model),
173 url: clientUrl,
174 params: {
175 id: null
176 },
177 data: {
178 model: model.name
179 },
180 component: 'xosCrud',
181 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800182
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800183 try {
184 this.XosRuntimeStates.addState(
185 this.stateNameFromModel(model),
186 state
187 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800188
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800189 // extend model
190 model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
191
192 d.resolve(model);
193 } catch (e) {
194 d.reject(e);
195 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800196 return d.promise;
197 }
198
199 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
200 const d = this.$q.defer();
201
202 const stateName = this.stateNameFromModel(model);
203
204 const parentState: string = this.getParentStateFromModel(model);
205
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800206 try {
207 this.XosNavigationService.add({
208 label: this.ConfigHelpers.pluralize(model.name),
209 state: stateName,
210 parent: parentState
211 });
212 d.resolve(model);
213 } catch (e) {
214 d.reject(e);
215 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800216
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800217
218 return d.promise;
219 }
220
221 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
222 const d = this.$q.defer();
223
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800224 const apiUrl = this.getApiUrlFromModel(model);
225 this.XosModelStore.query(model.name, apiUrl)
226 .subscribe(
227 () => {
Matteo Scandolo38e94a82017-03-02 12:17:27 -0800228 return d.resolve(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800229 },
230 err => {
Matteo Scandolo042ea632017-03-01 19:02:34 -0800231 return d.reject(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800232 }
233 );
234
235 return d.promise;
236 }
237
238 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
239
240 const d = this.$q.defer();
241
242 const stateUrl = this.stateNameFromModel(model);
243
244 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
245
246 d.resolve(model);
247
248 return d.promise;
249 }
250
251 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
252
253 const d = this.$q.defer();
254
255 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
256
257 d.resolve(model);
258
259 return d.promise;
260 }
261
262 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
263
264 const d = this.$q.defer();
265
266 if (!_.find(this.xosModels, m => m.name === model.name)) {
267 this.xosModels.push(model);
268 }
269
270 d.resolve(model);
271
272 return d.promise;
273 }
274}