blob: bab0873cee2617449578d0ae3325961c73b88dd7 [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;
40}
41
42// Service
43export interface IXosModelDiscovererService {
44 discover(): ng.IPromise<boolean>;
45 get(modelName: string): IXosModel;
Matteo Scandolo47c53fc2017-03-23 14:11:32 -070046 getApiUrlFromModel(model: IXosModel): string;
Matteo Scandolo9b460042017-04-14 16:24:45 -070047 areModelsLoaded(): boolean;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080048}
49
50export class XosModelDiscovererService implements IXosModelDiscovererService {
51 static $inject = [
52 '$log',
53 '$q',
54 'XosModelDefs',
55 'ConfigHelpers',
56 'XosRuntimeStates',
57 'XosNavigationService',
Matteo Scandolo042ea632017-03-01 19:02:34 -080058 'XosModelStore',
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070059 'ngProgressFactory',
60 'AuthService'
Matteo Scandolo1aee1982017-02-17 08:33:23 -080061 ];
62 private xosModels: IXosModel[] = []; // list of augmented model definitions;
63 private xosServices: string[] = []; // list of loaded services
Matteo Scandolo042ea632017-03-01 19:02:34 -080064 private progressBar;
Matteo Scandolo9b460042017-04-14 16:24:45 -070065 private modelsLoaded: boolean = false;
Matteo Scandolo1aee1982017-02-17 08:33:23 -080066
67 constructor (
68 private $log: ng.ILogService,
69 private $q: ng.IQService,
70 private XosModelDefs: IXosModeldefsService,
71 private ConfigHelpers: IXosConfigHelpersService,
72 private XosRuntimeStates: IXosRuntimeStatesService,
73 private XosNavigationService: IXosNavigationService,
Matteo Scandolo042ea632017-03-01 19:02:34 -080074 private XosModelStore: IXosModelStoreService,
Matteo Scandolo0f3692e2017-07-10 14:06:41 -070075 private ngProgressFactory: any, // check for type defs
76 private AuthService: IXosAuthService
Matteo Scandolo1aee1982017-02-17 08:33:23 -080077 ) {
Matteo Scandolo042ea632017-03-01 19:02:34 -080078 this.progressBar = this.ngProgressFactory.createInstance();
79 this.progressBar.setColor('#f6a821');
Matteo Scandolo1aee1982017-02-17 08:33:23 -080080 }
81
Matteo Scandolo9b460042017-04-14 16:24:45 -070082 public areModelsLoaded(): boolean {
83 return this.modelsLoaded;
84 }
85
Matteo Scandolo1aee1982017-02-17 08:33:23 -080086 public get(modelName: string): IXosModel|null {
87 return _.find(this.xosModels, m => m.name === modelName);
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 {
95 const serviceName = this.serviceNameFromAppName(model.app);
96 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[]) => {
105
106 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
156 private serviceNameFromAppName(appName: string): string {
157 return appName.replace('services.', '');
158 }
159
160 private stateNameFromModel(model: IXosModel): string {
161 return `xos.${this.serviceNameFromAppName(model.app)}.${model.name.toLowerCase()}`;
162 }
163
164 private getParentStateFromModel(model: IXosModel): string {
165 let parentState: string;
166 if (model.app === 'core') {
167 parentState = 'xos.core';
168 }
169 else {
170 const serviceName = this.addService(model);
171 parentState = `xos.${serviceName}`;
172 }
173 return parentState;
174 }
175
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800176 // add a service state and navigation item if it is not already there
177 private addService(model: IXosModel): string {
178 const serviceName: string = this.serviceNameFromAppName(model.app);
179 if (!_.find(this.xosServices, n => n === serviceName)) {
180 const serviceState = {
181 url: serviceName,
182 parent: 'xos',
183 abstract: true,
184 template: '<div ui-view></div>'
185 };
186 this.XosRuntimeStates.addState(`xos.${serviceName}`, serviceState);
187
188 this.XosNavigationService.add({
189 label: this.ConfigHelpers.toLabel(serviceName),
190 state: `xos.${serviceName}`,
191 });
192 this.xosServices.push(serviceName);
193 }
194 return serviceName;
195 }
196
197 private addState(model: IXosModel): ng.IPromise<IXosModel> {
198 const d = this.$q.defer();
199 const clientUrl = `/${this.ConfigHelpers.pluralize(model.name.toLowerCase())}/:id?`;
200 const state: IXosState = {
201 parent: this.getParentStateFromModel(model),
202 url: clientUrl,
203 params: {
204 id: null
205 },
206 data: {
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700207 model: model.name,
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800208 },
209 component: 'xosCrud',
210 };
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800211
Matteo Scandolo5d962a32017-08-01 18:16:14 -0700212 if (angular.isDefined(model.relations)) {
213 state.data.relations = model.relations;
214 }
215
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800216 try {
217 this.XosRuntimeStates.addState(
218 this.stateNameFromModel(model),
219 state
220 );
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800221
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800222 // extend model
223 model.clientUrl = `${this.serviceNameFromAppName(model.app)}${clientUrl}`;
224
225 d.resolve(model);
226 } catch (e) {
227 d.reject(e);
228 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800229 return d.promise;
230 }
231
232 private addNavItem(model: IXosModel): ng.IPromise<IXosModel> {
233 const d = this.$q.defer();
234
235 const stateName = this.stateNameFromModel(model);
236
237 const parentState: string = this.getParentStateFromModel(model);
238
Matteo Scandoloc80ccbd2017-02-27 16:24:33 -0800239 try {
240 this.XosNavigationService.add({
241 label: this.ConfigHelpers.pluralize(model.name),
242 state: stateName,
243 parent: parentState
244 });
245 d.resolve(model);
246 } catch (e) {
247 d.reject(e);
248 }
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800249
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800250
251 return d.promise;
252 }
253
254 private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
255 const d = this.$q.defer();
256
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800257 const apiUrl = this.getApiUrlFromModel(model);
258 this.XosModelStore.query(model.name, apiUrl)
259 .subscribe(
260 () => {
Matteo Scandolo38e94a82017-03-02 12:17:27 -0800261 return d.resolve(model);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800262 },
263 err => {
Matteo Scandolo0f3692e2017-07-10 14:06:41 -0700264 this.AuthService.handleUnauthenticatedRequest(err);
Matteo Scandolo042ea632017-03-01 19:02:34 -0800265 return d.reject(err);
Matteo Scandolo1aee1982017-02-17 08:33:23 -0800266 }
267 );
268
269 return d.promise;
270 }
271
272 private getTableCfg(model: IXosModel): ng.IPromise<IXosModel> {
273
274 const d = this.$q.defer();
275
276 const stateUrl = this.stateNameFromModel(model);
277
278 model.tableCfg = this.ConfigHelpers.modelToTableCfg(model, stateUrl);
279
280 d.resolve(model);
281
282 return d.promise;
283 }
284
285 private getFormCfg(model: IXosModel): ng.IPromise<IXosModel> {
286
287 const d = this.$q.defer();
288
289 model.formCfg = this.ConfigHelpers.modelToFormCfg(model);
290
291 d.resolve(model);
292
293 return d.promise;
294 }
295
296 private storeModel(model: IXosModel): ng.IPromise<IXosModel> {
297
298 const d = this.$q.defer();
299
300 if (!_.find(this.xosModels, m => m.name === model.name)) {
301 this.xosModels.push(model);
302 }
303
304 d.resolve(model);
305
306 return d.promise;
307 }
308}