blob: c6dcb7e8bebb6b92eae12464c704c8b45e7ddd4f [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 Scandolo1aee1982017-02-17 08:33:23 -080030
31export interface IXosModel {
32 name: string; // the model name
33 app: string; // the service to wich it belong
34 fields: IXosModelDefsField[];
35 relations?: IXosModelDefsRelation[];
36 backendUrl?: string; // the api endpoint
37 clientUrl?: string; // the view url
38 tableCfg?: IXosTableCfg;
39 formCfg?: IXosFormCfg;
Matteo Scandolo580033a2017-08-17 11:16:39 -070040 description: string;
41 verbose_name: string;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080042}
43
44// Service
45export interface IXosModelDiscovererService {
46 discover(): ng.IPromise<boolean>;
47 get(modelName: string): IXosModel;
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',
62 'AuthService'
Matteo Scandolo1aee1982017-02-17 08:33:23 -080063 ];
64 private xosModels: IXosModel[] = []; // list of augmented model definitions;
65 private xosServices: string[] = []; // list of loaded services
Matteo Scandolo042ea632017-03-01 19:02:34 -080066 private progressBar;
Matteo Scandolo9b460042017-04-14 16:24:45 -070067 private modelsLoaded: boolean = false;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080068
69 constructor (
70 private $log: ng.ILogService,
71 private $q: ng.IQService,
72 private XosModelDefs: IXosModeldefsService,
73 private ConfigHelpers: IXosConfigHelpersService,
74 private XosRuntimeStates: IXosRuntimeStatesService,
75 private XosNavigationService: IXosNavigationService,
Matteo Scandolo042ea632017-03-01 19:02:34 -080076 private XosModelStore: IXosModelStoreService,
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070077 private ngProgressFactory: any, // check for type defs
78 private AuthService: IXosAuthService
Matteo Scandolo1aee1982017-02-17 08:33:23 -080079 ) {
Matteo Scandolo042ea632017-03-01 19:02:34 -080080 this.progressBar = this.ngProgressFactory.createInstance();
81 this.progressBar.setColor('#f6a821');
Matteo Scandolo1aee1982017-02-17 08:33:23 -080082 }
83
Matteo Scandolo9b460042017-04-14 16:24:45 -070084 public areModelsLoaded(): boolean {
85 return this.modelsLoaded;
86 }
87
Matteo Scandolo1aee1982017-02-17 08:33:23 -080088 public get(modelName: string): IXosModel|null {
89 return _.find(this.xosModels, m => m.name === modelName);
90 }
91
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070092 public getApiUrlFromModel(model: IXosModel): string {
93 if (model.app === 'core') {
94 return `/core/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
95 }
96 else {
97 const serviceName = this.serviceNameFromAppName(model.app);
98 return `/${serviceName}/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}`;
99 }
100 }
101
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800102 public discover() {
103 const d = this.$q.defer();
Matteo Scandolo042ea632017-03-01 19:02:34 -0800104 this.progressBar.start();
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800105 this.XosModelDefs.get()
106 .then((modelsDef: IXosModeldef[]) => {
107
108 const pArray = [];
109 _.forEach(modelsDef, (model: IXosModeldef) => {
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800110 this.$log.debug(`[XosModelDiscovererService] Loading: ${model.name}`);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800111 let p = this.cacheModelEntries(model)
112 .then(model => {
113 return this.addState(model);
114 })
115 .then(model => {
116 return this.addNavItem(model);
117 })
118 .then(model => {
119 return this.getTableCfg(model);
120 })
121 .then(model => {
122 return this.getFormCfg(model);
123 })
124 .then(model => {
125 return this.storeModel(model);
126 })
127 .then(model => {
128 this.$log.debug(`[XosModelDiscovererService] Model ${model.name} stored`);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800129 return this.$q.resolve('true');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800130 })
131 .catch(err => {
Matteo Scandolo02229382017-04-18 11:52:23 -0700132 this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`, err);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800133 return this.$q.resolve('false');
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800134 });
135 pArray.push(p);
136 });
137 this.$q.all(pArray)
Matteo Scandolo042ea632017-03-01 19:02:34 -0800138 .then((res) => {
139 // the Model Loader promise won't ever be reject, in case it will be resolve with value false,
140 // that's because we want to wait anyway for all the models to be loaded
141 if (res.indexOf('false') > -1) {
Matteo Scandolo02229382017-04-18 11:52:23 -0700142 return d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800143 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800144 d.resolve(true);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800145 })
Matteo Scandolo02229382017-04-18 11:52:23 -0700146 .catch((e) => {
147 this.$log.error(`[XosModelDiscovererService]`, e);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800148 d.resolve(false);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800149 })
150 .finally(() => {
151 this.progressBar.complete();
Matteo Scandolo9b460042017-04-14 16:24:45 -0700152 this.modelsLoaded = true;
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800153 });
154 });
155 return d.promise;
156 }
157
158 private serviceNameFromAppName(appName: string): string {
159 return appName.replace('services.', '');
160 }
161
162 private stateNameFromModel(model: IXosModel): string {
163 return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
164 }
165
166 private getParentStateFromModel(model: IXosModel): string {
167 let parentState: string;
168 if (model.app === 'core') {
169 parentState = 'xos.core';
170 }
171 else {
172 const serviceName = this.addService(model);
173 parentState = `xos.${serviceName}`;
174 }
175 return parentState;
176 }
177
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800178 // add a service state and navigation item if it is not already there
179 private addService(model: IXosModel): string {
180 const serviceName: string = this.serviceNameFromAppName(model.app);
181 if (!_.find(this.xosServices, n => n === serviceName)) {
182 const serviceState = {
183 url: serviceName,
184 parent: 'xos',
185 abstract: true,
186 template: '<div ui-view></div>'
187 };
188 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
189
190 this.XosNavigationService.add({
191 label: this.ConfigHelpers.toLabel(serviceName),
192 state: `xos.${serviceName}`,
193 });
194 this.xosServices.push(serviceName);
195 }
196 return serviceName;
197 }
198
199 private addState(model: IXosModel): ng.IPromise<IXosModel> {
200 const d = this.$q.defer();
201 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
202 const state: IXosState = {
203 parent: this.getParentStateFromModel(model),
204 url: clientUrl,
205 params: {
206 id: null
207 },
208 data: {
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700209 model: model.name,
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800210 },
211 component: 'xosCrud',
212 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800213
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700214 if (angular.isDefined(model.relations)) {
215 state.data.relations = model.relations;
216 }
217
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800218 try {
219 this.XosRuntimeStates.addState(
220 this.stateNameFromModel(model),
221 state
222 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800223
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800224 // extend model
225 model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
226
227 d.resolve(model);
228 } catch (e) {
229 d.reject(e);
230 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800231 return d.promise;
232 }
233
234 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
235 const d = this.$q.defer();
236
237 const stateName = this.stateNameFromModel(model);
238
239 const parentState: string = this.getParentStateFromModel(model);
240
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800241 try {
Matteo Scandolo580033a2017-08-17 11:16:39 -0700242 const name = model.verbose_name ? model.verbose_name : model.name;
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800243 this.XosNavigationService.add({
Matteo Scandolo580033a2017-08-17 11:16:39 -0700244 label: this.ConfigHelpers.pluralize(name),
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800245 state: stateName,
246 parent: parentState
247 });
248 d.resolve(model);
249 } catch (e) {
250 d.reject(e);
251 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800252
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800253
254 return d.promise;
255 }
256
257 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
258 const d = this.$q.defer();
259
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800260 const apiUrl = this.getApiUrlFromModel(model);
261 this.XosModelStore.query(model.name, apiUrl)
262 .subscribe(
263 () => {
Matteo Scandolo38e94a82017-03-02 12:17:27 -0800264 return d.resolve(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800265 },
266 err => {
Matteo Scandolo0f3692e2017-07-10 14:06:41 -0700267 this.AuthService.handleUnauthenticatedRequest(err);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800268 return d.reject(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800269 }
270 );
271
272 return d.promise;
273 }
274
275 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
276
277 const d = this.$q.defer();
278
279 const stateUrl = this.stateNameFromModel(model);
280
281 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
282
283 d.resolve(model);
284
285 return d.promise;
286 }
287
288 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
289
290 const d = this.$q.defer();
291
292 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
293
294 d.resolve(model);
295
296 return d.promise;
297 }
298
299 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
300
301 const d = this.$q.defer();
302
303 if (!_.find(this.xosModels, m => m.name === model.name)) {
304 this.xosModels.push(model);
305 }
306
307 d.resolve(model);
308
309 return d.promise;
310 }
311}