blob: ba9cb1339147602d125fd03c8b43cb3585d4a74f [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 Scandolo9b460042017-04-14 16:24:45 -070028 areModelsLoaded(): boolean;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080029}
30
31export class XosModelDiscovererService implements IXosModelDiscovererService {
32 static $inject = [
33 '$log',
34 '$q',
35 'XosModelDefs',
36 'ConfigHelpers',
37 'XosRuntimeStates',
38 'XosNavigationService',
Matteo Scandolo042ea632017-03-01 19:02:34 -080039 'XosModelStore',
40 'ngProgressFactory'
Matteo Scandolo1aee1982017-02-17 08:33:23 -080041 ];
42 private xosModels: IXosModel[] = []; // list of augmented model definitions;
43 private xosServices: string[] = []; // list of loaded services
Matteo Scandolo042ea632017-03-01 19:02:34 -080044 private progressBar;
Matteo Scandolo9b460042017-04-14 16:24:45 -070045 private modelsLoaded: boolean = false;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080046
47 constructor (
48 private $log: ng.ILogService,
49 private $q: ng.IQService,
50 private XosModelDefs: IXosModeldefsService,
51 private ConfigHelpers: IXosConfigHelpersService,
52 private XosRuntimeStates: IXosRuntimeStatesService,
53 private XosNavigationService: IXosNavigationService,
Matteo Scandolo042ea632017-03-01 19:02:34 -080054 private XosModelStore: IXosModelStoreService,
55 private ngProgressFactory: any // check for type defs
Matteo Scandolo1aee1982017-02-17 08:33:23 -080056 ) {
Matteo Scandolo042ea632017-03-01 19:02:34 -080057 this.progressBar = this.ngProgressFactory.createInstance();
58 this.progressBar.setColor('#f6a821');
Matteo Scandolo1aee1982017-02-17 08:33:23 -080059 }
60
Matteo Scandolo9b460042017-04-14 16:24:45 -070061 public areModelsLoaded(): boolean {
62 return this.modelsLoaded;
63 }
64
Matteo Scandolo1aee1982017-02-17 08:33:23 -080065 public get(modelName: string): IXosModel|null {
66 return _.find(this.xosModels, m => m.name === modelName);
67 }
68
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070069 public getApiUrlFromModel(model: IXosModel): string {
70 if (model.app === 'core') {
71 return `/core/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
72 }
73 else {
74 const serviceName = this.serviceNameFromAppName(model.app);
75 return `/${serviceName}/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
76 }
77 }
78
Matteo Scandolo1aee1982017-02-17 08:33:23 -080079 public discover() {
80 const d = this.$q.defer();
Matteo Scandolo042ea632017-03-01 19:02:34 -080081 this.progressBar.start();
Matteo Scandolo1aee1982017-02-17 08:33:23 -080082 this.XosModelDefs.get()
83 .then((modelsDef: IXosModeldef[]) => {
84
85 const pArray = [];
86 _.forEach(modelsDef, (model: IXosModeldef) => {
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -080087 this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
Matteo Scandolo1aee1982017-02-17 08:33:23 -080088 let p = this.cacheModelEntries(model)
89 .then(model => {
90 return this.addState(model);
91 })
92 .then(model => {
93 return this.addNavItem(model);
94 })
95 .then(model => {
96 return this.getTableCfg(model);
97 })
98 .then(model => {
99 return this.getFormCfg(model);
100 })
101 .then(model => {
102 return this.storeModel(model);
103 })
104 .then(model => {
105 this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800106 return this.$q.resolve('true');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800107 })
108 .catch(err => {
109 this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800110 return this.$q.resolve('false');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800111 });
112 pArray.push(p);
113 });
114 this.$q.all(pArray)
Matteo Scandolo042ea632017-03-01 19:02:34 -0800115 .then((res) => {
116 // the Model Loader promise won't ever be reject, in case it will be resolve with value false,
117 // that's because we want to wait anyway for all the models to be loaded
118 if (res.indexOf('false') > -1) {
119 d.resolve(false);
120 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800121 d.resolve(true);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800122 })
123 .catch(() => {
124 d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800125 })
126 .finally(() => {
127 this.progressBar.complete();
Matteo Scandolo9b460042017-04-14 16:24:45 -0700128 this.modelsLoaded = true;
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800129 });
130 });
131 return d.promise;
132 }
133
134 private serviceNameFromAppName(appName: string): string {
135 return appName.replace('services.', '');
136 }
137
138 private stateNameFromModel(model: IXosModel): string {
139 return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
140 }
141
142 private getParentStateFromModel(model: IXosModel): string {
143 let parentState: string;
144 if (model.app === 'core') {
145 parentState = 'xos.core';
146 }
147 else {
148 const serviceName = this.addService(model);
149 parentState = `xos.${serviceName}`;
150 }
151 return parentState;
152 }
153
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800154 // add a service state and navigation item if it is not already there
155 private addService(model: IXosModel): string {
156 const serviceName: string = this.serviceNameFromAppName(model.app);
157 if (!_.find(this.xosServices, n => n === serviceName)) {
158 const serviceState = {
159 url: serviceName,
160 parent: 'xos',
161 abstract: true,
162 template: '<div ui-view></div>'
163 };
164 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
165
166 this.XosNavigationService.add({
167 label: this.ConfigHelpers.toLabel(serviceName),
168 state: `xos.${serviceName}`,
169 });
170 this.xosServices.push(serviceName);
171 }
172 return serviceName;
173 }
174
175 private addState(model: IXosModel): ng.IPromise<IXosModel> {
176 const d = this.$q.defer();
177 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
178 const state: IXosState = {
179 parent: this.getParentStateFromModel(model),
180 url: clientUrl,
181 params: {
182 id: null
183 },
184 data: {
185 model: model.name
186 },
187 component: 'xosCrud',
188 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800189
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800190 try {
191 this.XosRuntimeStates.addState(
192 this.stateNameFromModel(model),
193 state
194 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800195
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800196 // extend model
197 model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
198
199 d.resolve(model);
200 } catch (e) {
201 d.reject(e);
202 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800203 return d.promise;
204 }
205
206 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
207 const d = this.$q.defer();
208
209 const stateName = this.stateNameFromModel(model);
210
211 const parentState: string = this.getParentStateFromModel(model);
212
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800213 try {
214 this.XosNavigationService.add({
215 label: this.ConfigHelpers.pluralize(model.name),
216 state: stateName,
217 parent: parentState
218 });
219 d.resolve(model);
220 } catch (e) {
221 d.reject(e);
222 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800223
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800224
225 return d.promise;
226 }
227
228 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
229 const d = this.$q.defer();
230
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800231 const apiUrl = this.getApiUrlFromModel(model);
232 this.XosModelStore.query(model.name, apiUrl)
233 .subscribe(
234 () => {
Matteo Scandolo38e94a82017-03-02 12:17:27 -0800235 return d.resolve(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800236 },
237 err => {
Matteo Scandolo042ea632017-03-01 19:02:34 -0800238 return d.reject(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800239 }
240 );
241
242 return d.promise;
243 }
244
245 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
246
247 const d = this.$q.defer();
248
249 const stateUrl = this.stateNameFromModel(model);
250
251 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
252
253 d.resolve(model);
254
255 return d.promise;
256 }
257
258 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
259
260 const d = this.$q.defer();
261
262 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
263
264 d.resolve(model);
265
266 return d.promise;
267 }
268
269 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
270
271 const d = this.$q.defer();
272
273 if (!_.find(this.xosModels, m => m.name === model.name)) {
274 this.xosModels.push(model);
275 }
276
277 d.resolve(model);
278
279 return d.promise;
280 }
281}