re base code
[sdc.git] / catalog-ui / src / app / view-models / dashboard / dashboard-view-model.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 'use strict';
22 import {IConfigRoles, IAppConfigurtaion, IAppMenu, IUserProperties, Component} from "app/models";
23 import {EntityService, SharingService, CacheService} from "app/services";
24 import {ComponentType, ResourceType, MenuHandler, ModalsHandler, ChangeLifecycleStateHandler, SEVERITY, ComponentFactory, CHANGE_COMPONENT_CSAR_VERSION_FLAG} from "app/utils";
25 import {IClientMessageModalModel} from "../modals/message-modal/message-client-modal/client-message-modal-view-model";
26 import {UserService} from "../../ng2/services/user.service";
27
28
29 export interface IDashboardViewModelScope extends ng.IScope {
30
31     isLoading:boolean;
32     numberOfItemToDisplay:number;
33     components:Array<Component>;
34     folders:FoldersMenu;
35     roles:IConfigRoles;
36     user:IUserProperties;
37     sdcConfig:IAppConfigurtaion;
38     sdcMenu:IAppMenu;
39     sharingService:SharingService;
40     showTutorial:boolean;
41     isFirstTime:boolean;
42     version:string;
43     filterParams:DashboardFilter;
44     vfcmtType:string;
45
46     changeFilterParams():void;
47     updateSearchTerm(newTerm:string):void;
48     onImportVfc(file:any):void;
49     onImportVf(file:any):void;
50     openCreateModal(componentType:ComponentType, importedFile:any):void;
51     openWhatsNewModal(version:string):void;
52     openDesignerModal(isResource:boolean, uniqueId:string):void;
53     setSelectedFolder(folderItem:FoldersItemsMenu):void;
54     entitiesCount(folderItem:FoldersItemsMenu):number;
55     getCurrentFolderDistributed():Array<Component>;
56     changeLifecycleState(entity:any, data:any):void;
57     goToComponent(component:Component):void;
58     raiseNumberOfElementToDisplay():void;
59     wizardDebugEdit:Function;
60     notificationIconCallback:Function;
61 }
62
63 interface ICheckboxesFilter {
64     // Statuses
65     selectedStatuses:Array<string>;
66     // distributed
67     distributed:Array<string>;
68 }
69
70 export interface IItemMenu {
71
72 }
73
74 export interface IMenuItemProperties {
75     text:string;
76     group:string;
77     state:string;
78     dist:string;
79     groupname:string;
80     states:Array<any>;
81 }
82
83 export interface IQueryFilterParams {
84     'filter.term': string;
85     'filter.distributed': string;
86     'filter.status': string
87 }
88
89
90 export class DashboardFilter {
91     searchTerm: string;
92     checkboxes: ICheckboxesFilter;
93
94     constructor(params = {}) {
95         this.searchTerm = params['filter.term'] || "";
96         this.checkboxes = {
97             selectedStatuses : params['filter.status']? params['filter.status'].split(',') : [],
98             distributed : params['filter.distributed']? params['filter.distributed'].split(',') : []
99         };
100     }
101
102     public toParam = ():IQueryFilterParams => {
103         return {
104             'filter.term': this.searchTerm,
105             'filter.distributed': this.checkboxes && this.checkboxes.distributed.join(',') || null,
106             'filter.status': this.checkboxes && this.checkboxes.selectedStatuses.join(',') || null
107         };
108     }
109
110 }
111
112 export class FoldersMenu {
113
114     private _folders:Array<FoldersItemsMenu> = [];
115
116     constructor(folders:Array<IMenuItemProperties>) {
117         let self = this;
118         folders.forEach(function (folder:IMenuItemProperties) {
119             if (folder.groupname) {
120                 self._folders.push(new FoldersItemsMenuGroup(folder));
121             } else {
122                 self._folders.push(new FoldersItemsMenu(folder));
123             }
124         });
125         self._folders[0].setSelected(true);
126     }
127
128     public getFolders = ():Array<FoldersItemsMenu> => {
129         return this._folders;
130     };
131
132     public getCurrentFolder = ():FoldersItemsMenu => {
133         let menuItem:FoldersItemsMenu = undefined;
134         this.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) {
135             if (tmpFolder.isSelected()) {
136                 menuItem = tmpFolder;
137             }
138         });
139         return menuItem;
140     };
141
142     public setSelected = (folder:FoldersItemsMenu):void => {
143         this.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) {
144             tmpFolder.setSelected(false);
145         });
146         folder.setSelected(true);
147     }
148
149 }
150
151 export class FoldersItemsMenu implements IItemMenu {
152
153     public text:string;
154     public group:string;
155     public state:string;
156     public dist:string;
157     public states:Array<any>;
158
159     private selected:boolean = false;
160
161     constructor(menuProperties:IMenuItemProperties) {
162         this.text = menuProperties.text;
163         this.group = menuProperties.group;
164         this.state = menuProperties.state;
165         this.states = menuProperties.states;
166         this.dist = menuProperties.dist;
167     }
168
169     public isSelected = ():boolean => {
170         return this.selected;
171     };
172
173     public setSelected = (value:boolean):void => {
174         this.selected = value;
175     };
176
177     public isGroup = ():boolean => {
178         return false;
179     }
180
181 }
182
183 export class FoldersItemsMenuGroup extends FoldersItemsMenu {
184
185     public groupname:string;
186
187     constructor(menuProperties:IMenuItemProperties) {
188         super(menuProperties);
189         this.groupname = menuProperties.groupname;
190     }
191
192     public isGroup = ():boolean => {
193         return true;
194     }
195
196 }
197
198 export class DashboardViewModel {
199     static '$inject' = [
200         '$scope',
201         '$filter',
202         'Sdc.Services.EntityService',
203         '$http',
204         'sdcConfig',
205         'sdcMenu',
206         '$state',
207         '$stateParams',
208         'UserServiceNg2',
209         'Sdc.Services.SharingService',
210         'Sdc.Services.CacheService',
211         '$q',
212         'ComponentFactory',
213         'ChangeLifecycleStateHandler',
214         'ModalsHandler',
215         'MenuHandler'
216     ];
217
218     private components:Array<Component>;
219
220     constructor(private $scope:IDashboardViewModelScope,
221                 private $filter:ng.IFilterService,
222                 private entityService:EntityService,
223                 private $http:ng.IHttpService,
224                 private sdcConfig:IAppConfigurtaion,
225                 private sdcMenu:IAppMenu,
226                 private $state:ng.ui.IStateService,
227                 private $stateParams:any,
228                 private userService:UserService,
229                 private sharingService:SharingService,
230                 private cacheService:CacheService,
231                 private $q:ng.IQService,
232                 private ComponentFactory:ComponentFactory,
233                 private ChangeLifecycleStateHandler:ChangeLifecycleStateHandler,
234                 private ModalsHandler:ModalsHandler,
235                 private MenuHandler:MenuHandler) {
236         this.initScope();
237         this.initFolders();
238         this.initEntities();
239
240         if (this.$stateParams) {
241
242             if (this.$state.params.folder) {
243                 let self = this;
244                 let folderName = this.$state.params.folder.replaceAll("_", " ");
245
246                 this.$scope.folders.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) {
247                     if (tmpFolder.text === folderName) {
248                         self.$scope.setSelectedFolder(tmpFolder);
249                     }
250                 });
251             }
252
253             // Show the tutorial if needed when the dashboard page is opened.<script src="bower_components/angular-filter/dist/angular-filter.min.js"></script>
254             // This is called from the welcome page.
255             else if (this.$stateParams.show === 'tutorial') {
256                 this.$scope.showTutorial = true;
257                 this.$scope.isFirstTime = true;
258             }
259         }
260     }
261
262     private initFolders = ():void => {
263         if (this.$scope.user) {
264             this.$scope.folders = new FoldersMenu(this.$scope.roles[this.$scope.user.role].folder);
265         }
266     };
267
268     private initScope = ():void => {
269         let self = this;
270
271         this.$scope.version = this.cacheService.get('version');
272         this.$scope.sharingService = this.sharingService;
273         this.$scope.numberOfItemToDisplay = 0;
274         this.$scope.isLoading = false;
275         this.$scope.sdcConfig = this.sdcConfig;
276         this.$scope.sdcMenu = this.sdcMenu;
277         this.$scope.user = this.userService.getLoggedinUser();
278         this.$scope.roles = this.sdcMenu.roles;
279         this.$scope.showTutorial = false;
280         this.$scope.isFirstTime = false;
281         this.$scope.vfcmtType = ResourceType.VFCMT;
282         this.$scope.filterParams = new DashboardFilter(this.$state.params);
283
284         // Open onboarding modal
285         this.$scope.notificationIconCallback = ():void => {
286             this.ModalsHandler.openOnboadrdingModal('Import').then((result)=> {
287                 //OK
288                 if(!result.previousComponent || result.previousComponent.csarVersion != result.componentCsar.csarVersion) {
289                     this.cacheService.set(CHANGE_COMPONENT_CSAR_VERSION_FLAG, result.componentCsar.csarVersion);
290                 }
291
292                 this.$state.go('workspace.general', {
293                     id: result.previousComponent && result.previousComponent.uniqueId,
294                     componentCsar: result.componentCsar,
295                     type: result.type
296                 });
297             }, ()=> {
298                 // ERROR
299             });
300         };
301
302         this.$scope.onImportVf = (file:any):void => {
303             if (file && file.filename) {
304                 // Check that the file has valid extension.
305                 let fileExtension:string = file.filename.split(".").pop();
306                 if (this.sdcConfig.csarFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
307                     this.$state.go('workspace.general', {
308                         type: ComponentType.RESOURCE.toLowerCase(),
309                         importedFile: file,
310                         resourceType: ResourceType.VF
311                     });
312                 } else {
313                     let data:IClientMessageModalModel = {
314                         title: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS_TITLE"),
315                         message: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_CSAR_EXTENSIONS", "{'extensions': '" + this.sdcConfig.csarFileExtension + "'}"),
316                         severity: SEVERITY.ERROR
317                     };
318                     this.ModalsHandler.openClientMessageModal(data);
319                 }
320             }
321         };
322
323         this.$scope.onImportVfc = (file:any):void => {
324             if (file && file.filename) {
325                 // Check that the file has valid extension.
326                 let fileExtension:string = file.filename.split(".").pop();
327                 if (this.sdcConfig.toscaFileExtension.indexOf(fileExtension.toLowerCase()) !== -1) {
328                     this.$state.go('workspace.general', {
329                         type: ComponentType.RESOURCE.toLowerCase(),
330                         importedFile: file,
331                         resourceType: ResourceType.VFC
332                     });
333                 } else {
334                     let data:IClientMessageModalModel = {
335                         title: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS_TITLE"),
336                         message: self.$filter('translate')("NEW_SERVICE_RESOURCE_ERROR_VALID_TOSCA_EXTENSIONS", "{'extensions': '" + this.sdcConfig.toscaFileExtension + "'}"),
337                         severity: SEVERITY.ERROR
338                     };
339                     this.ModalsHandler.openClientMessageModal(data);
340                 }
341             }
342         };
343
344         this.$scope.openCreateModal = (componentType:string, importedFile:any):void => {
345             if (importedFile) {
346                 this.initEntities(true); // Return from import
347             } else {
348                 this.$state.go('workspace.general', {type: componentType.toLowerCase()});
349             }
350
351         };
352
353         this.$scope.createPNF = ():void => {
354             this.$state.go('workspace.general', {
355                 type: ComponentType.RESOURCE.toLowerCase(),
356                 resourceType: ResourceType.PNF
357             });
358         };
359
360         this.$scope.createCR = ():void => {
361             this.$state.go('workspace.general', {
362                 type: ComponentType.RESOURCE.toLowerCase(),
363                 resourceType: ResourceType.CR
364             });
365         };
366
367         this.$scope.entitiesCount = (folderItem:FoldersItemsMenu):any => {
368             let self = this;
369             let total:number = 0;
370             if (folderItem.isGroup()) {
371                 this.$scope.folders.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) {
372                     if (tmpFolder.group && tmpFolder.group === (<FoldersItemsMenuGroup>folderItem).groupname) {
373                         total = total + self._getTotalCounts(tmpFolder, self);
374                     }
375                 });
376             } else {
377                 total = total + self._getTotalCounts(folderItem, self);
378             }
379             return total;
380         };
381
382         this.$scope.getCurrentFolderDistributed = ():Array<any> => {
383             let self = this;
384             let states = [];
385             if (this.$scope.folders) {
386                 let folderItem:FoldersItemsMenu = this.$scope.folders.getCurrentFolder();
387                 if (folderItem.isGroup()) {
388                     this.$scope.folders.getFolders().forEach(function (tmpFolder:FoldersItemsMenu) {
389                         if (tmpFolder.group && tmpFolder.group === (<FoldersItemsMenuGroup>folderItem).groupname) {
390                             self._setStates(tmpFolder, states);
391                         }
392                     });
393                 } else {
394                     self._setStates(folderItem, states);
395                 }
396             }
397             return states;
398         };
399
400         this.$scope.setSelectedFolder = (folderItem:FoldersItemsMenu):void => {
401             this.$scope.folders.setSelected(folderItem);
402         };
403
404         this.$scope.goToComponent = (component:Component):void => {
405             this.$scope.isLoading = true;
406             this.$state.go('workspace.general', {id: component.uniqueId, type: component.componentType.toLowerCase()});
407         };
408
409         this.$scope.raiseNumberOfElementToDisplay = ():void => {
410             this.$scope.numberOfItemToDisplay = this.$scope.numberOfItemToDisplay + 35;
411             if (this.$scope.components) {
412                 this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.components.length;
413             }
414         };
415
416         this.$scope.updateSearchTerm = (newTerm: string):void => {
417             this.$scope.filterParams.searchTerm = newTerm;
418         };
419
420         this.$scope.changeFilterParams = ():void => {
421             this.$state.go('.', this.$scope.filterParams.toParam(), {location: 'replace', notify: false});
422         };
423     };
424
425     private _getTotalCounts(tmpFolder, self):number {
426         let total:number = 0;
427         if (tmpFolder.dist !== undefined) {
428             let distributions = tmpFolder.dist.split(',');
429             distributions.forEach((item:any) => {
430                 total = total + self.getEntitiesByStateDist(tmpFolder.state, item).length;
431             });
432         }
433         else {
434             total = total + self.getEntitiesByStateDist(tmpFolder.state, tmpFolder.dist).length;
435         }
436         return total;
437     }
438
439     private _setStates(tmpFolder, states) {
440         if (tmpFolder.states !== undefined) {
441             tmpFolder.states.forEach(function (item:any) {
442                 states.push({"state": item.state, "dist": item.dist});
443             });
444         } else {
445             states.push({"state": tmpFolder.state, "dist": tmpFolder.dist});
446         }
447     }
448
449     private initEntities = (forceReload?:boolean):void => {
450
451         if(forceReload || this.componentShouldReload()){
452             this.$scope.isLoading = true;
453             this.entityService.getAllComponents(true).then(
454                 (components:Array<Component>) => {
455                     this.cacheService.set('breadcrumbsComponentsState', this.$state.current.name);  //dashboard
456                     this.cacheService.set('breadcrumbsComponents', components);
457                     this.components = components;
458                     this.$scope.components = components;
459                     this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.components.length;
460                     this.$scope.isLoading = false;
461                 });
462         } else {
463             this.components = this.cacheService.get('breadcrumbsComponents');
464             this.$scope.components = this.components;
465             this.$scope.isAllItemDisplay = this.$scope.numberOfItemToDisplay >= this.$scope.components.length;
466             
467         }
468     
469     };
470
471     private isDefaultFilter = (): boolean => {
472         let defaultFilter = new DashboardFilter();
473         return angular.equals(defaultFilter, this.$scope.filterParams);
474     }
475
476     private componentShouldReload = ():boolean => {
477         let breadcrumbsValid: boolean = (this.$state.current.name === this.cacheService.get('breadcrumbsComponentsState') && this.cacheService.contains('breadcrumbsComponents'));
478         return !breadcrumbsValid || this.isDefaultFilter();
479     }
480
481     private getEntitiesByStateDist = (state:string, dist:string):Array<Component> => {
482         let gObj:Array<Component>;
483         if (this.components && (state || dist)) {
484             gObj = this.components.filter(function (obj:Component) {
485                 if (dist !== undefined && obj.distributionStatus === dist && obj.lifecycleState === state) {
486                     return true;
487                 } else if (dist === undefined && obj.lifecycleState === state) {
488                     return true;
489                 }
490                 return false;
491             });
492         } else {
493             gObj = [];
494         }
495         return gObj;
496     }
497 }