blob: 0addc05278bbe37955fa5b02d3125896dc98972b [file] [log] [blame]
Matteo Scandolofb46ae62017-08-08 09:10:50 -07001
2/*
3 * Copyright 2017-present Open Networking Foundation
4
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8
9 * http://www.apache.org/licenses/LICENSE-2.0
10
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
Matteo Scandolo1aee1982017-02-17 08:33:23 -080019// TODO test me hard!!!
20
21import * as _ from 'lodash';
22import {IXosModeldefsService, IXosModeldef, IXosModelDefsField, IXosModelDefsRelation} from '../rest/modeldefs.rest';
23import {IXosTableCfg} from '../../core/table/table';
24import {IXosFormCfg} from '../../core/form/form';
25import {IXosNavigationService} from '../../core/services/navigation';
26import {IXosConfigHelpersService} from '../../core/services/helpers/config.helpers';
27import {IXosRuntimeStatesService, IXosState} from '../../core/services/runtime-states';
28import {IXosModelStoreService} from '../stores/model.store';
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070029import {IXosAuthService} from '../rest/auth.rest';
Matteo Scandolo0e171f32017-09-26 17:21:41 -070030import {IXosModeldefsCache} from './modeldefs.service';
Matteo Scandolo1aee1982017-02-17 08:33:23 -080031
32export interface IXosModel {
33 name: string; // the model name
34 app: string; // the service to wich it belong
35 fields: IXosModelDefsField[];
36 relations?: IXosModelDefsRelation[];
37 backendUrl?: string; // the api endpoint
38 clientUrl?: string; // the view url
39 tableCfg?: IXosTableCfg;
40 formCfg?: IXosFormCfg;
Matteo Scandolo580033a2017-08-17 11:16:39 -070041 description: string;
42 verbose_name: string;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080043}
44
45// Service
46export interface IXosModelDiscovererService {
47 discover(): ng.IPromise<boolean>;
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070048 getApiUrlFromModel(model: IXosModel): string;
Matteo Scandolo9b460042017-04-14 16:24:45 -070049 areModelsLoaded(): boolean;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080050}
51
52export class XosModelDiscovererService implements IXosModelDiscovererService {
53 static $inject = [
54 '$log',
55 '$q',
56 'XosModelDefs',
57 'ConfigHelpers',
58 'XosRuntimeStates',
59 'XosNavigationService',
Matteo Scandolo042ea632017-03-01 19:02:34 -080060 'XosModelStore',
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070061 'ngProgressFactory',
Matteo Scandolo0e171f32017-09-26 17:21:41 -070062 'AuthService',
63 'XosModeldefsCache'
Matteo Scandolo1aee1982017-02-17 08:33:23 -080064 ];
Matteo Scandolo0e171f32017-09-26 17:21:41 -070065
Matteo Scandolo1aee1982017-02-17 08:33:23 -080066 private xosServices: string[] = []; // list of loaded services
Matteo Scandolo042ea632017-03-01 19:02:34 -080067 private progressBar;
Matteo Scandolo9b460042017-04-14 16:24:45 -070068 private modelsLoaded: boolean = false;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080069
70 constructor (
71 private $log: ng.ILogService,
72 private $q: ng.IQService,
73 private XosModelDefs: IXosModeldefsService,
74 private ConfigHelpers: IXosConfigHelpersService,
75 private XosRuntimeStates: IXosRuntimeStatesService,
76 private XosNavigationService: IXosNavigationService,
Matteo Scandolo042ea632017-03-01 19:02:34 -080077 private XosModelStore: IXosModelStoreService,
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070078 private ngProgressFactory: any, // check for type defs
Matteo Scandolo0e171f32017-09-26 17:21:41 -070079 private AuthService: IXosAuthService,
80 private XosModeldefsCache: IXosModeldefsCache
Matteo Scandolo1aee1982017-02-17 08:33:23 -080081 ) {
Matteo Scandolo042ea632017-03-01 19:02:34 -080082 this.progressBar = this.ngProgressFactory.createInstance();
83 this.progressBar.setColor('#f6a821');
Matteo Scandolo1aee1982017-02-17 08:33:23 -080084 }
85
Matteo Scandolo9b460042017-04-14 16:24:45 -070086 public areModelsLoaded(): boolean {
87 return this.modelsLoaded;
88 }
89
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070090 public getApiUrlFromModel(model: IXosModel): string {
91 if (model.app === 'core') {
92 return `/core/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
93 }
94 else {
Matteo Scandolo0e171f32017-09-26 17:21:41 -070095 const serviceName = this.XosModeldefsCache.serviceNameFromAppName(model.app);
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070096 return `/${serviceName}/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
97 }
98 }
99
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800100 public discover() {
101 const d = this.$q.defer();
Matteo Scandolo042ea632017-03-01 19:02:34 -0800102 this.progressBar.start();
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800103 this.XosModelDefs.get()
104 .then((modelsDef: IXosModeldef[]) => {
Matteo Scandolo0e171f32017-09-26 17:21:41 -0700105 // TODO store modeldefs and add a method to retrieve the model definition from the name
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800106 const pArray = [];
107 _.forEach(modelsDef, (model: IXosModeldef) => {
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800108 this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800109 let p = this.cacheModelEntries(model)
110 .then(model => {
111 return this.addState(model);
112 })
113 .then(model => {
114 return this.addNavItem(model);
115 })
116 .then(model => {
117 return this.getTableCfg(model);
118 })
119 .then(model => {
120 return this.getFormCfg(model);
121 })
122 .then(model => {
123 return this.storeModel(model);
124 })
125 .then(model => {
126 this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800127 return this.$q.resolve('true');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800128 })
129 .catch(err => {
Matteo Scandolo02229382017-04-18 11:52:23 -0700130 this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`, err);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800131 return this.$q.resolve('false');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800132 });
133 pArray.push(p);
134 });
135 this.$q.all(pArray)
Matteo Scandolo042ea632017-03-01 19:02:34 -0800136 .then((res) => {
137 // the Model Loader promise won't ever be reject, in case it will be resolve with value false,
138 // that's because we want to wait anyway for all the models to be loaded
139 if (res.indexOf('false') > -1) {
Matteo Scandolo02229382017-04-18 11:52:23 -0700140 return d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800141 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800142 d.resolve(true);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800143 })
Matteo Scandolo02229382017-04-18 11:52:23 -0700144 .catch((e) => {
145 this.$log.error(`[XosModelDiscovererService]`, e);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800146 d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800147 })
148 .finally(() => {
149 this.progressBar.complete();
Matteo Scandolo9b460042017-04-14 16:24:45 -0700150 this.modelsLoaded = true;
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800151 });
152 });
153 return d.promise;
154 }
155
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800156 private stateNameFromModel(model: IXosModel): string {
Matteo Scandolo0e171f32017-09-26 17:21:41 -0700157 return `xos.${this.XosModeldefsCache.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800158 }
159
160 private getParentStateFromModel(model: IXosModel): string {
161 let parentState: string;
162 if (model.app === 'core') {
163 parentState = 'xos.core';
164 }
165 else {
166 const serviceName = this.addService(model);
167 parentState = `xos.${serviceName}`;
168 }
169 return parentState;
170 }
171
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800172 // add a service state and navigation item if it is not already there
173 private addService(model: IXosModel): string {
Matteo Scandolo0e171f32017-09-26 17:21:41 -0700174 const serviceName: string = this.XosModeldefsCache.serviceNameFromAppName(model.app);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800175 if (!_.find(this.xosServices, n => n === serviceName)) {
176 const serviceState = {
177 url: serviceName,
178 parent: 'xos',
179 abstract: true,
180 template: '<div ui-view></div>'
181 };
182 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
183
184 this.XosNavigationService.add({
185 label: this.ConfigHelpers.toLabel(serviceName),
186 state: `xos.${serviceName}`,
187 });
188 this.xosServices.push(serviceName);
189 }
190 return serviceName;
191 }
192
193 private addState(model: IXosModel): ng.IPromise<IXosModel> {
194 const d = this.$q.defer();
195 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
196 const state: IXosState = {
197 parent: this.getParentStateFromModel(model),
198 url: clientUrl,
199 params: {
200 id: null
201 },
202 data: {
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700203 model: model.name,
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800204 },
205 component: 'xosCrud',
206 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800207
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700208 if (angular.isDefined(model.relations)) {
209 state.data.relations = model.relations;
210 }
211
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800212 try {
213 this.XosRuntimeStates.addState(
214 this.stateNameFromModel(model),
215 state
216 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800217
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800218 // extend model
Matteo Scandolo0e171f32017-09-26 17:21:41 -0700219 model.clientUrl = `${this.XosModeldefsCache.serviceNameFromAppName(model.app)}${clientUrl}`;
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800220
221 d.resolve(model);
222 } catch (e) {
223 d.reject(e);
224 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800225 return d.promise;
226 }
227
228 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
229 const d = this.$q.defer();
230
231 const stateName = this.stateNameFromModel(model);
232
233 const parentState: string = this.getParentStateFromModel(model);
234
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800235 try {
Matteo Scandolo580033a2017-08-17 11:16:39 -0700236 const name = model.verbose_name ? model.verbose_name : model.name;
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800237 this.XosNavigationService.add({
Matteo Scandolo580033a2017-08-17 11:16:39 -0700238 label: this.ConfigHelpers.pluralize(name),
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800239 state: stateName,
240 parent: parentState
241 });
242 d.resolve(model);
243 } catch (e) {
244 d.reject(e);
245 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800246
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800247
248 return d.promise;
249 }
250
251 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
252 const d = this.$q.defer();
253
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800254 const apiUrl = this.getApiUrlFromModel(model);
255 this.XosModelStore.query(model.name, apiUrl)
256 .subscribe(
257 () => {
Matteo Scandolo38e94a82017-03-02 12:17:27 -0800258 return d.resolve(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800259 },
260 err => {
Matteo Scandolo0f3692e2017-07-10 14:06:41 -0700261 this.AuthService.handleUnauthenticatedRequest(err);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800262 return d.reject(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800263 }
264 );
265
266 return d.promise;
267 }
268
269 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
270
271 const d = this.$q.defer();
272
273 const stateUrl = this.stateNameFromModel(model);
274
275 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
276
277 d.resolve(model);
278
279 return d.promise;
280 }
281
282 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
283
284 const d = this.$q.defer();
285
286 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
287
288 d.resolve(model);
289
290 return d.promise;
291 }
292
293 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
294
295 const d = this.$q.defer();
296
Matteo Scandolo0e171f32017-09-26 17:21:41 -0700297 this.XosModeldefsCache.cache(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800298
299 d.resolve(model);
300
301 return d.promise;
302 }
303}