blob: 37ecc93f299c47ab07da9d05bbaac3d513bd42f4 [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';
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070011import {IXosAuthService} from '../rest/auth.rest';
Matteo Scandolo1aee1982017-02-17 08:33:23 -080012
13export interface IXosModel {
14 name: string; // the model name
15 app: string; // the service to wich it belong
16 fields: IXosModelDefsField[];
17 relations?: IXosModelDefsRelation[];
18 backendUrl?: string; // the api endpoint
19 clientUrl?: string; // the view url
20 tableCfg?: IXosTableCfg;
21 formCfg?: IXosFormCfg;
22}
23
24// Service
25export interface IXosModelDiscovererService {
26 discover(): ng.IPromise<boolean>;
27 get(modelName: string): IXosModel;
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070028 getApiUrlFromModel(model: IXosModel): string;
Matteo Scandolo9b460042017-04-14 16:24:45 -070029 areModelsLoaded(): boolean;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080030}
31
32export class XosModelDiscovererService implements IXosModelDiscovererService {
33 static $inject = [
34 '$log',
35 '$q',
36 'XosModelDefs',
37 'ConfigHelpers',
38 'XosRuntimeStates',
39 'XosNavigationService',
Matteo Scandolo042ea632017-03-01 19:02:34 -080040 'XosModelStore',
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070041 'ngProgressFactory',
42 'AuthService'
Matteo Scandolo1aee1982017-02-17 08:33:23 -080043 ];
44 private xosModels: IXosModel[] = []; // list of augmented model definitions;
45 private xosServices: string[] = []; // list of loaded services
Matteo Scandolo042ea632017-03-01 19:02:34 -080046 private progressBar;
Matteo Scandolo9b460042017-04-14 16:24:45 -070047 private modelsLoaded: boolean = false;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080048
49 constructor (
50 private $log: ng.ILogService,
51 private $q: ng.IQService,
52 private XosModelDefs: IXosModeldefsService,
53 private ConfigHelpers: IXosConfigHelpersService,
54 private XosRuntimeStates: IXosRuntimeStatesService,
55 private XosNavigationService: IXosNavigationService,
Matteo Scandolo042ea632017-03-01 19:02:34 -080056 private XosModelStore: IXosModelStoreService,
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070057 private ngProgressFactory: any, // check for type defs
58 private AuthService: IXosAuthService
Matteo Scandolo1aee1982017-02-17 08:33:23 -080059 ) {
Matteo Scandolo042ea632017-03-01 19:02:34 -080060 this.progressBar = this.ngProgressFactory.createInstance();
61 this.progressBar.setColor('#f6a821');
Matteo Scandolo1aee1982017-02-17 08:33:23 -080062 }
63
Matteo Scandolo9b460042017-04-14 16:24:45 -070064 public areModelsLoaded(): boolean {
65 return this.modelsLoaded;
66 }
67
Matteo Scandolo1aee1982017-02-17 08:33:23 -080068 public get(modelName: string): IXosModel|null {
69 return _.find(this.xosModels, m => m.name === modelName);
70 }
71
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070072 public getApiUrlFromModel(model: IXosModel): string {
73 if (model.app === 'core') {
74 return `/core/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
75 }
76 else {
77 const serviceName = this.serviceNameFromAppName(model.app);
78 return `/${serviceName}/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
79 }
80 }
81
Matteo Scandolo1aee1982017-02-17 08:33:23 -080082 public discover() {
83 const d = this.$q.defer();
Matteo Scandolo042ea632017-03-01 19:02:34 -080084 this.progressBar.start();
Matteo Scandolo1aee1982017-02-17 08:33:23 -080085 this.XosModelDefs.get()
86 .then((modelsDef: IXosModeldef[]) => {
87
88 const pArray = [];
89 _.forEach(modelsDef, (model: IXosModeldef) => {
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -080090 this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
Matteo Scandolo1aee1982017-02-17 08:33:23 -080091 let p = this.cacheModelEntries(model)
92 .then(model => {
93 return this.addState(model);
94 })
95 .then(model => {
96 return this.addNavItem(model);
97 })
98 .then(model => {
99 return this.getTableCfg(model);
100 })
101 .then(model => {
102 return this.getFormCfg(model);
103 })
104 .then(model => {
105 return this.storeModel(model);
106 })
107 .then(model => {
108 this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800109 return this.$q.resolve('true');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800110 })
111 .catch(err => {
Matteo Scandolo02229382017-04-18 11:52:23 -0700112 this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`, err);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800113 return this.$q.resolve('false');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800114 });
115 pArray.push(p);
116 });
117 this.$q.all(pArray)
Matteo Scandolo042ea632017-03-01 19:02:34 -0800118 .then((res) => {
119 // the Model Loader promise won't ever be reject, in case it will be resolve with value false,
120 // that's because we want to wait anyway for all the models to be loaded
121 if (res.indexOf('false') > -1) {
Matteo Scandolo02229382017-04-18 11:52:23 -0700122 return d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800123 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800124 d.resolve(true);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800125 })
Matteo Scandolo02229382017-04-18 11:52:23 -0700126 .catch((e) => {
127 this.$log.error(`[XosModelDiscovererService]`, e);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800128 d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800129 })
130 .finally(() => {
131 this.progressBar.complete();
Matteo Scandolo9b460042017-04-14 16:24:45 -0700132 this.modelsLoaded = true;
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800133 });
134 });
135 return d.promise;
136 }
137
138 private serviceNameFromAppName(appName: string): string {
139 return appName.replace('services.', '');
140 }
141
142 private stateNameFromModel(model: IXosModel): string {
143 return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
144 }
145
146 private getParentStateFromModel(model: IXosModel): string {
147 let parentState: string;
148 if (model.app === 'core') {
149 parentState = 'xos.core';
150 }
151 else {
152 const serviceName = this.addService(model);
153 parentState = `xos.${serviceName}`;
154 }
155 return parentState;
156 }
157
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800158 // add a service state and navigation item if it is not already there
159 private addService(model: IXosModel): string {
160 const serviceName: string = this.serviceNameFromAppName(model.app);
161 if (!_.find(this.xosServices, n => n === serviceName)) {
162 const serviceState = {
163 url: serviceName,
164 parent: 'xos',
165 abstract: true,
166 template: '<div ui-view></div>'
167 };
168 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
169
170 this.XosNavigationService.add({
171 label: this.ConfigHelpers.toLabel(serviceName),
172 state: `xos.${serviceName}`,
173 });
174 this.xosServices.push(serviceName);
175 }
176 return serviceName;
177 }
178
179 private addState(model: IXosModel): ng.IPromise<IXosModel> {
180 const d = this.$q.defer();
181 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
182 const state: IXosState = {
183 parent: this.getParentStateFromModel(model),
184 url: clientUrl,
185 params: {
186 id: null
187 },
188 data: {
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700189 model: model.name,
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800190 },
191 component: 'xosCrud',
192 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800193
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700194 if (angular.isDefined(model.relations)) {
195 state.data.relations = model.relations;
196 }
197
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800198 try {
199 this.XosRuntimeStates.addState(
200 this.stateNameFromModel(model),
201 state
202 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800203
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800204 // extend model
205 model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
206
207 d.resolve(model);
208 } catch (e) {
209 d.reject(e);
210 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800211 return d.promise;
212 }
213
214 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
215 const d = this.$q.defer();
216
217 const stateName = this.stateNameFromModel(model);
218
219 const parentState: string = this.getParentStateFromModel(model);
220
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800221 try {
222 this.XosNavigationService.add({
223 label: this.ConfigHelpers.pluralize(model.name),
224 state: stateName,
225 parent: parentState
226 });
227 d.resolve(model);
228 } catch (e) {
229 d.reject(e);
230 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800231
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800232
233 return d.promise;
234 }
235
236 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
237 const d = this.$q.defer();
238
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800239 const apiUrl = this.getApiUrlFromModel(model);
240 this.XosModelStore.query(model.name, apiUrl)
241 .subscribe(
242 () => {
Matteo Scandolo38e94a82017-03-02 12:17:27 -0800243 return d.resolve(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800244 },
245 err => {
Matteo Scandolo0f3692e2017-07-10 14:06:41 -0700246 this.AuthService.handleUnauthenticatedRequest(err);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800247 return d.reject(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800248 }
249 );
250
251 return d.promise;
252 }
253
254 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
255
256 const d = this.$q.defer();
257
258 const stateUrl = this.stateNameFromModel(model);
259
260 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
261
262 d.resolve(model);
263
264 return d.promise;
265 }
266
267 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
268
269 const d = this.$q.defer();
270
271 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
272
273 d.resolve(model);
274
275 return d.promise;
276 }
277
278 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
279
280 const d = this.$q.defer();
281
282 if (!_.find(this.xosModels, m => m.name === model.name)) {
283 this.xosModels.push(model);
284 }
285
286 d.resolve(model);
287
288 return d.promise;
289 }
290}