blob: da6f26570127959eae68bc55f56ab2eae3655a2b [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;
27}
28
29export class XosModelDiscovererService implements IXosModelDiscovererService {
30 static $inject = [
31 '$log',
32 '$q',
33 'XosModelDefs',
34 'ConfigHelpers',
35 'XosRuntimeStates',
36 'XosNavigationService',
37 'XosModelStore'
38 ];
39 private xosModels: IXosModel[] = []; // list of augmented model definitions;
40 private xosServices: string[] = []; // list of loaded services
41
42 constructor (
43 private $log: ng.ILogService,
44 private $q: ng.IQService,
45 private XosModelDefs: IXosModeldefsService,
46 private ConfigHelpers: IXosConfigHelpersService,
47 private XosRuntimeStates: IXosRuntimeStatesService,
48 private XosNavigationService: IXosNavigationService,
49 private XosModelStore: IXosModelStoreService
50 ) {
51 }
52
53 public get(modelName: string): IXosModel|null {
54 return _.find(this.xosModels, m => m.name === modelName);
55 }
56
57 public discover() {
58 const d = this.$q.defer();
59
60 this.XosModelDefs.get()
61 .then((modelsDef: IXosModeldef[]) => {
62
63 const pArray = [];
64 _.forEach(modelsDef, (model: IXosModeldef) => {
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -080065 this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
Matteo Scandolo1aee1982017-02-17 08:33:23 -080066 let p = this.cacheModelEntries(model)
67 .then(model => {
68 return this.addState(model);
69 })
70 .then(model => {
71 return this.addNavItem(model);
72 })
73 .then(model => {
74 return this.getTableCfg(model);
75 })
76 .then(model => {
77 return this.getFormCfg(model);
78 })
79 .then(model => {
80 return this.storeModel(model);
81 })
82 .then(model => {
83 this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
84 return this.$q.resolve();
85 })
86 .catch(err => {
87 this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`);
88 // NOTE why this does not resolve?????
89 // return this.$q.resolve();
90 return this.$q.reject();
91 });
92 pArray.push(p);
93 });
94 this.$q.all(pArray)
95 .then(() => {
96 d.resolve(true);
97 this.$log.info('[XosModelDiscovererService] All models loaded!');
98 })
99 .catch(() => {
100 d.resolve(false);
101 });
102 });
103 return d.promise;
104 }
105
106 private serviceNameFromAppName(appName: string): string {
107 return appName.replace('services.', '');
108 }
109
110 private stateNameFromModel(model: IXosModel): string {
111 return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
112 }
113
114 private getParentStateFromModel(model: IXosModel): string {
115 let parentState: string;
116 if (model.app === 'core') {
117 parentState = 'xos.core';
118 }
119 else {
120 const serviceName = this.addService(model);
121 parentState = `xos.${serviceName}`;
122 }
123 return parentState;
124 }
125
126 private getApiUrlFromModel(model: IXosModel): string {
127 if (model.app === 'core') {
128 return `/core/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
129 }
130 else {
131 const serviceName = this.serviceNameFromAppName(model.app);
132 return `/${serviceName}/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
133 }
134 }
135
136 // add a service state and navigation item if it is not already there
137 private addService(model: IXosModel): string {
138 const serviceName: string = this.serviceNameFromAppName(model.app);
139 if (!_.find(this.xosServices, n => n === serviceName)) {
140 const serviceState = {
141 url: serviceName,
142 parent: 'xos',
143 abstract: true,
144 template: '<div ui-view></div>'
145 };
146 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
147
148 this.XosNavigationService.add({
149 label: this.ConfigHelpers.toLabel(serviceName),
150 state: `xos.${serviceName}`,
151 });
152 this.xosServices.push(serviceName);
153 }
154 return serviceName;
155 }
156
157 private addState(model: IXosModel): ng.IPromise<IXosModel> {
158 const d = this.$q.defer();
159 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
160 const state: IXosState = {
161 parent: this.getParentStateFromModel(model),
162 url: clientUrl,
163 params: {
164 id: null
165 },
166 data: {
167 model: model.name
168 },
169 component: 'xosCrud',
170 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800171
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800172 try {
173 this.XosRuntimeStates.addState(
174 this.stateNameFromModel(model),
175 state
176 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800177
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800178 // extend model
179 model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
180
181 d.resolve(model);
182 } catch (e) {
183 d.reject(e);
184 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800185 return d.promise;
186 }
187
188 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
189 const d = this.$q.defer();
190
191 const stateName = this.stateNameFromModel(model);
192
193 const parentState: string = this.getParentStateFromModel(model);
194
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800195 try {
196 this.XosNavigationService.add({
197 label: this.ConfigHelpers.pluralize(model.name),
198 state: stateName,
199 parent: parentState
200 });
201 d.resolve(model);
202 } catch (e) {
203 d.reject(e);
204 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800205
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800206
207 return d.promise;
208 }
209
210 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
211 const d = this.$q.defer();
212
213 let called = false;
214 const apiUrl = this.getApiUrlFromModel(model);
215 this.XosModelStore.query(model.name, apiUrl)
216 .subscribe(
217 () => {
218 // skipping the first response as the observable gets created as an empty array
219 if (called) {
220 return d.resolve(model);
221 }
222 called = true;
223 },
224 err => {
225 d.reject(err);
226 }
227 );
228
229 return d.promise;
230 }
231
232 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
233
234 const d = this.$q.defer();
235
236 const stateUrl = this.stateNameFromModel(model);
237
238 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
239
240 d.resolve(model);
241
242 return d.promise;
243 }
244
245 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
246
247 const d = this.$q.defer();
248
249 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
250
251 d.resolve(model);
252
253 return d.promise;
254 }
255
256 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
257
258 const d = this.$q.defer();
259
260 if (!_.find(this.xosModels, m => m.name === model.name)) {
261 this.xosModels.push(model);
262 }
263
264 d.resolve(model);
265
266 return d.promise;
267 }
268}