blob: 4306825cbe06e4b09e12c97d345ab52b754f9997 [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 => {
Matteo Scandolo02229382017-04-18 11:52:23 -0700109 this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`, err);
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) {
Matteo Scandolo02229382017-04-18 11:52:23 -0700119 return d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800120 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800121 d.resolve(true);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800122 })
Matteo Scandolo02229382017-04-18 11:52:23 -0700123 .catch((e) => {
124 this.$log.error(`[XosModelDiscovererService]`, e);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800125 d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800126 })
127 .finally(() => {
128 this.progressBar.complete();
Matteo Scandolo9b460042017-04-14 16:24:45 -0700129 this.modelsLoaded = true;
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800130 });
131 });
132 return d.promise;
133 }
134
135 private serviceNameFromAppName(appName: string): string {
136 return appName.replace('services.', '');
137 }
138
139 private stateNameFromModel(model: IXosModel): string {
140 return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
141 }
142
143 private getParentStateFromModel(model: IXosModel): string {
144 let parentState: string;
145 if (model.app === 'core') {
146 parentState = 'xos.core';
147 }
148 else {
149 const serviceName = this.addService(model);
150 parentState = `xos.${serviceName}`;
151 }
152 return parentState;
153 }
154
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800155 // add a service state and navigation item if it is not already there
156 private addService(model: IXosModel): string {
157 const serviceName: string = this.serviceNameFromAppName(model.app);
158 if (!_.find(this.xosServices, n => n === serviceName)) {
159 const serviceState = {
160 url: serviceName,
161 parent: 'xos',
162 abstract: true,
163 template: '<div ui-view></div>'
164 };
165 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
166
167 this.XosNavigationService.add({
168 label: this.ConfigHelpers.toLabel(serviceName),
169 state: `xos.${serviceName}`,
170 });
171 this.xosServices.push(serviceName);
172 }
173 return serviceName;
174 }
175
176 private addState(model: IXosModel): ng.IPromise<IXosModel> {
177 const d = this.$q.defer();
178 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
179 const state: IXosState = {
180 parent: this.getParentStateFromModel(model),
181 url: clientUrl,
182 params: {
183 id: null
184 },
185 data: {
186 model: model.name
187 },
188 component: 'xosCrud',
189 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800190
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800191 try {
192 this.XosRuntimeStates.addState(
193 this.stateNameFromModel(model),
194 state
195 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800196
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800197 // extend model
198 model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
199
200 d.resolve(model);
201 } catch (e) {
202 d.reject(e);
203 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800204 return d.promise;
205 }
206
207 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
208 const d = this.$q.defer();
209
210 const stateName = this.stateNameFromModel(model);
211
212 const parentState: string = this.getParentStateFromModel(model);
213
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800214 try {
215 this.XosNavigationService.add({
216 label: this.ConfigHelpers.pluralize(model.name),
217 state: stateName,
218 parent: parentState
219 });
220 d.resolve(model);
221 } catch (e) {
222 d.reject(e);
223 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800224
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800225
226 return d.promise;
227 }
228
229 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
230 const d = this.$q.defer();
231
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800232 const apiUrl = this.getApiUrlFromModel(model);
233 this.XosModelStore.query(model.name, apiUrl)
234 .subscribe(
235 () => {
Matteo Scandolo38e94a82017-03-02 12:17:27 -0800236 return d.resolve(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800237 },
238 err => {
Matteo Scandolo042ea632017-03-01 19:02:34 -0800239 return d.reject(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800240 }
241 );
242
243 return d.promise;
244 }
245
246 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
247
248 const d = this.$q.defer();
249
250 const stateUrl = this.stateNameFromModel(model);
251
252 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
253
254 d.resolve(model);
255
256 return d.promise;
257 }
258
259 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
260
261 const d = this.$q.defer();
262
263 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
264
265 d.resolve(model);
266
267 return d.promise;
268 }
269
270 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
271
272 const d = this.$q.defer();
273
274 if (!_.find(this.xosModels, m => m.name === model.name)) {
275 this.xosModels.push(model);
276 }
277
278 d.resolve(model);
279
280 return d.promise;
281 }
282}