CSIT Fix for SDC-2585
[sdc.git] / catalog-ui / src / app / view-models / workspace / tabs / req-and-capabilities / req-and-capabilities-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 /**
22  * Created by rcohen on 9/22/2016.
23  */
24 'use strict';
25 import * as _ from "lodash";
26 import {ComponentRef} from '@angular/core';
27 import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
28 import {ModalsHandler, ResourceType} from "app/utils";
29 import {ComponentType} from "app/utils/constants";
30 import {
31     Capability, PropertyModel, Requirement, Resource,
32     RelationshipTypesMap, NodeTypesMap, CapabilityTypesMap
33 } from "app/models";
34 import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response";
35 import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service";
36 import {ToscaTypesServiceNg2} from "app/ng2/services/tosca-types.service";
37 import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
38 import {ModalService} from 'app/ng2/services/modal.service';
39 import {RequirementsEditorComponent} from 'app/ng2/pages/req-and-capabilities-editor/requirements-editor/requirements-editor.component';
40 import {CapabilitiesEditorComponent} from 'app/ng2/pages/req-and-capabilities-editor/capabilities-editor/capabilities-editor.component';
41 import {ModalService as ModalServiceSdcUI} from "sdc-ui/lib/angular/modals/modal.service";
42 import {IModalConfig} from "sdc-ui/lib/angular/modals/models/modal-config";
43 import {ModalButtonComponent} from "sdc-ui/lib/angular/components";
44
45 export class SortTableDefined {
46     reverse:boolean;
47     sortByField:string;
48 }
49
50 class RequirementUI extends Requirement {
51     isCreatedManually: boolean;
52
53     constructor(input: Requirement, componentUniqueId: string) {
54         super(input);
55         this.isCreatedManually = input.ownerId === componentUniqueId;
56     }
57 }
58 class CapabilityUI extends Capability {
59     isCreatedManually: boolean;
60
61     constructor(input: Capability, componentUniqueId: string) {
62         super(input);
63         this.isCreatedManually = input.ownerId === componentUniqueId;
64     }
65 }
66
67 interface IReqAndCapabilitiesViewModelScope extends IWorkspaceViewModelScope {
68     requirementsTableHeadersList:Array<any>;
69     editableRequirementsTableHeadersList: Array<any>;
70     capabilitiesTableHeadersList:Array<any>;
71     editableCapabilitiesTableHeadersList: Array<any>;
72     capabilityPropertiesTableHeadersList:Array<any>;
73     requirementsSortTableDefined:SortTableDefined;
74     capabilitiesSortTableDefined:SortTableDefined;
75     propertiesSortTableDefined:SortTableDefined;
76     requirements: Array<RequirementUI>;
77     filteredRequirementsList: Array<RequirementUI>;
78     capabilities: Array<CapabilityUI>;
79     filteredCapabilitiesList: Array<CapabilityUI>;
80     mode:string;
81     filteredProperties:Array<Array<PropertyModel>>;
82     searchText:string;
83     isEditable: boolean;
84     modalInstance: ComponentRef<ModalComponent>;
85     filter: {txt: string; show: boolean};
86
87     sort(sortBy: string, sortByTableDefined: SortTableDefined, autoCollapseCapabilitiesRows: boolean): void;
88     sortByIsCreatedManually(arrToSort: Array<RequirementUI|CapabilityUI>): Array<any>;
89     updateProperty(property:PropertyModel, indexInFilteredProperties:number):void;
90     allCapabilitiesSelected(selected:boolean):void;
91     onAddBtnClicked(): void;
92     onEditRequirement(req: RequirementUI): void;
93     onEditCapability(cap: CapabilityUI): void;
94     onDeleteReq(event, req: RequirementUI): void;
95     onDeleteCap(event, cap: CapabilityUI): void;
96     onFilter(): void;
97     isListEmpty(): boolean;
98     onSwitchTab(): void;
99     onSearchIconClick(): void;
100     cutToscaTypePrefix(valToCut: string, textToStartCut: string): string;
101     isReadonly(): boolean;
102 }
103
104 export class ReqAndCapabilitiesViewModel {
105
106     static '$inject' = [
107         '$scope',
108         '$filter',
109         'ModalsHandler',
110         'ComponentServiceNg2',
111         'ToscaTypesServiceNg2',
112         'ModalServiceNg2',
113         'ModalServiceSdcUI'
114     ];
115
116
117     constructor(private $scope:IReqAndCapabilitiesViewModelScope,
118                 private $filter:ng.IFilterService,
119                 private ModalsHandler:ModalsHandler,
120                 private ComponentServiceNg2: ComponentServiceNg2,
121                 private ToscaTypesServiceNg2: ToscaTypesServiceNg2,
122                 private ModalServiceNg2: ModalService,
123                 private ModalServiceSdcUI: ModalServiceSdcUI) {
124
125         this.initCapabilitiesAndRequirements();
126         this.fetchCapabilitiesRelatedData();
127     }
128
129     private initCapabilitiesAndRequirements = (): void => {
130
131         this.$scope.isEditable = this.getIsEditableByComponentType();
132         this.$scope.isLoading = true;
133         this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.component.componentType, this.$scope.component.uniqueId).subscribe((response: ComponentGenericResponse) => {
134             this.$scope.component.capabilities = response.capabilities;
135             this.$scope.component.requirements = response.requirements;
136             this.initScope();
137             this.$scope.isLoading = false;
138         }, () => {
139             this.$scope.isLoading = false;
140         });
141
142     }
143
144     private openEditPropertyModal = (property:PropertyModel, indexInFilteredProperties:number):void => {
145         //...because there is not be api
146         _.forEach(this.$scope.filteredProperties[indexInFilteredProperties], (prop:PropertyModel)=> {
147             prop.readonly = true;
148         });
149         this.ModalsHandler.openEditPropertyModal(property, this.$scope.component, this.$scope.filteredProperties[indexInFilteredProperties], false, "component", this.$scope.component.uniqueId).then(() => {
150
151         });
152     };
153
154     private initScope = (currentMode = 'requirements'): void => {
155         this.$scope.isReadonly = (): boolean => {
156             return this.$scope.isViewMode() || !this.$scope.isDesigner();
157         };
158         this.$scope.filter = {txt: '', show: false};
159         this.$scope.requirementsSortTableDefined = {
160             reverse: false,
161             sortByField: this.$scope.isEditable ? 'other' : 'name'
162         };
163         this.$scope.capabilitiesSortTableDefined = {
164             reverse: false,
165             sortByField: this.$scope.isEditable ? 'other' : 'name'
166         };
167         this.$scope.propertiesSortTableDefined = {
168             reverse: false,
169             sortByField: 'name'
170         };
171
172         this.$scope.setValidState(true);
173         this.$scope.requirementsTableHeadersList = [
174             {title: 'Name', property: 'name'},
175             {title: 'Capability', property: 'capability'},
176             {title: 'Node', property: 'node'},
177             {title: 'Relationship', property: 'relationship'},
178             {title: 'Connected To', property: ''},
179             {title: 'Occurrences', property: ''}
180         ];
181         this.$scope.capabilitiesTableHeadersList = [
182             {title: 'Name', property: 'name'},
183             {title: 'Type', property: 'type'},
184             {title: 'Description', property: ''},
185             {title: 'Valid Source', property: ''},
186             {title: 'Occurrences', property: ''}
187         ];
188         this.$scope.editableRequirementsTableHeadersList = [
189             {title: 'Name', property: 'name'},
190             {title: 'Capability', property: 'capability'},
191             {title: 'Node', property: 'node'},
192             {title: 'Relationship', property: 'relationship'},
193             {title: 'Occurrences', property: 'occurrences'},
194             {title: '●●●', property: 'other'}
195         ];
196         this.$scope.editableCapabilitiesTableHeadersList = [
197             {title: 'Name', property: 'name'},
198             {title: 'Type', property: 'type'},
199             {title: 'Description', property: 'description'},
200             {title: 'Valid Sources', property: 'valid-sources'},
201             {title: 'Occurrences', property: 'occurrences'},
202             {title: '●●●', property: 'other'}
203         ];
204         this.$scope.capabilityPropertiesTableHeadersList = [
205             {title: 'Name', property: 'name'},
206             {title: 'Type', property: 'type'},
207             {title: 'Schema', property: 'schema.property.type'},
208             {title: 'Description', property: 'description'},
209         ];
210         this.$scope.filteredProperties = [];
211
212         this.$scope.mode = currentMode;
213         this.$scope.requirements = [];
214         _.forEach(this.$scope.component.requirements, (req:Array<Requirement>, capName)=> {
215             let reqUIList: Array<RequirementUI> = _.map(req, reqObj => new RequirementUI(reqObj, this.$scope.component.uniqueId));
216             this.$scope.requirements = this.$scope.requirements.concat(reqUIList);
217         });
218         this.$scope.filteredRequirementsList = this.$scope.requirements;
219
220         this.$scope.capabilities = [];
221         _.forEach(this.$scope.component.capabilities, (cap:Array<Capability>, capName)=> {
222             let capUIList: Array<CapabilityUI> = _.map(cap, capObj => new CapabilityUI(capObj, this.$scope.component.uniqueId));
223             this.$scope.capabilities = this.$scope.capabilities.concat(capUIList);
224         });
225
226         this.$scope.sortByIsCreatedManually = (arrToSort: Array<RequirementUI|CapabilityUI>): Array<any> => {
227             return arrToSort.sort((elem1: RequirementUI|CapabilityUI, elem2: RequirementUI|CapabilityUI) => +elem2.isCreatedManually - (+elem1.isCreatedManually));
228         };
229         this.$scope.filteredCapabilitiesList = this.$scope.sortByIsCreatedManually(this.$scope.capabilities);
230         this.$scope.filteredRequirementsList = this.$scope.sortByIsCreatedManually(this.$scope.requirements);
231
232         this.$scope.sort = (sortBy: string, sortByTableDefined: SortTableDefined, autoCollapseCapabilitiesRows: boolean): void => {
233             sortByTableDefined.reverse = (sortByTableDefined.sortByField === sortBy) ? !sortByTableDefined.reverse : false;
234             sortByTableDefined.sortByField = sortBy;
235             if (autoCollapseCapabilitiesRows) {
236                 this.$scope.allCapabilitiesSelected(false);
237             }
238         };
239
240         this.$scope.updateProperty = (property:PropertyModel, indexInFilteredProperties:number):void => {
241             this.openEditPropertyModal(property, indexInFilteredProperties);
242         };
243
244         this.$scope.allCapabilitiesSelected = (selected:boolean):void => {
245             _.forEach(this.$scope.capabilities, (cap:Capability)=> {
246                 cap.selected = selected;
247             });
248         };
249         this.$scope.onAddBtnClicked = (): void => {
250             switch (this.$scope.mode) {
251                 case 'requirements':
252                     this.openRequirementsModal();
253                     break;
254                 case 'capabilities':
255                     this.openCapabilitiesModal();
256                     break;
257             }
258         };
259         this.$scope.onEditRequirement = (req: RequirementUI): void => {
260             this.openRequirementsModal(req);
261         };
262         this.$scope.onEditCapability = (cap: CapabilityUI): void => {
263             this.openCapabilitiesModal(cap);
264         };
265         this.$scope.onDeleteReq = (event: Event, req: RequirementUI): void => {
266             event.stopPropagation();
267             this.ModalServiceSdcUI.openAlertModal('Delete Requirement',
268                 `Are you sure you want to delete requirement: ${req.name}?`, 'OK', () => this.deleteRequirement(req), 'Cancel');
269         };
270         this.$scope.onDeleteCap = (event: Event, cap: CapabilityUI): void => {
271             event.stopPropagation();
272             this.ModalServiceSdcUI.openAlertModal('Delete Capability',
273                 `Are you sure you want to delete capability: ${cap.name}?`, 'OK', () => this.deleteCapability(cap), 'Cancel');
274         };
275         this.$scope.onSearchIconClick = (): void => {
276             this.$scope.filter.show = !!this.$scope.filter.txt || !this.$scope.filter.show;
277         };
278         this.$scope.onFilter = (): void => {
279             switch (this.$scope.mode) {
280                 case 'requirements':
281                     this.$scope.filteredRequirementsList = _.filter(this.$scope.requirements, req => req.name.includes(this.$scope.filter.txt));
282                     break;
283                 case 'capabilities':
284                     this.$scope.filteredCapabilitiesList = _.filter(this.$scope.capabilities, cap => cap.name.includes(this.$scope.filter.txt));
285                     break;
286             }
287         };
288         this.$scope.isListEmpty = (): boolean => {
289             switch (this.$scope.mode) {
290                 case 'requirements':
291                     return this.$scope.requirements.length === 0;
292                 case 'capabilities':
293                     return this.$scope.capabilities.length === 0;
294             }
295         };
296         this.$scope.onSwitchTab = (): void => {
297             this.$scope.mode = this.$scope.mode === 'requirements' ? 'capabilities' : 'requirements';
298             this.$scope.filter.txt = '';
299             this.$scope.filter.show = false;
300             this.$scope.filteredRequirementsList = this.$scope.requirements;
301             this.$scope.filteredCapabilitiesList = this.$scope.capabilities;
302         };
303         this.$scope.cutToscaTypePrefix = (valToCut: string, textToStartCut: string): string => {
304             let index = valToCut.indexOf(textToStartCut);
305             return index !== -1 ? valToCut.substr(index + textToStartCut.length) : valToCut;
306         };
307     };
308
309     private getIsEditableByComponentType() {
310         if (this.$scope.componentType === ComponentType.SERVICE) {
311             return true;
312         }
313         if (this.$scope.component.isResource()) {
314             let componentAsResource: Resource = <Resource>this.$scope.component;
315             return componentAsResource.resourceType === ResourceType.VF ||
316                 componentAsResource.resourceType === ResourceType.PNF;
317         }
318         return false;
319     };
320
321     private fetchCapabilitiesRelatedData() {
322         if (this.$scope.isEditable) {
323             this.$scope.capabilityTypesList = [];
324             this.ToscaTypesServiceNg2.fetchCapabilityTypes().subscribe((result: CapabilityTypesMap) => {
325                 _.forEach(result, capabilityType => this.$scope.capabilityTypesList.push(capabilityType));
326             });
327             this.$scope.nodeTypesList = [];
328             this.ToscaTypesServiceNg2.fetchNodeTypes().subscribe((result: NodeTypesMap) => {
329                 _.forEach(result, nodeType => this.$scope.nodeTypesList.push(nodeType));
330             });
331             this.$scope.relationshipTypesList = [];
332             this.ToscaTypesServiceNg2.fetchRelationshipTypes().subscribe((result: RelationshipTypesMap) => {
333                 _.forEach(result, relshipType => this.$scope.relationshipTypesList.push(relshipType));
334             });
335         }
336     }
337
338     private openRequirementsModal(req?: RequirementUI) {
339         let modalConfig: IModalConfig = {
340             size: 'md',
341             title: (req ? 'Update' : 'Add') + ' Requirement',
342             type: 'custom',
343             buttons: [
344                 {
345                     id: 'saveButton',
346                     text: (req ? 'Update' : 'Create'),
347                     size: "'x-small'",
348                     callback: () => this.createOrUpdateRequirement(),
349                     closeModal: true
350                 },
351                 {text: "Cancel", size: "'x-small'", closeModal: true}]
352         };
353         let modalInputs = {
354             requirement: req,
355             relationshipTypesList: this.$scope.relationshipTypesList,
356             nodeTypesList: this.$scope.nodeTypesList,
357             capabilityTypesList: this.$scope.capabilityTypesList,
358             isReadonly: this.$scope.isViewMode() || !this.$scope.isDesigner(),
359             validityChangedCallback: this.getDisabled
360         };
361
362         this.ModalServiceSdcUI.openCustomModal(modalConfig, RequirementsEditorComponent, {input: modalInputs});
363     }
364
365     private openCapabilitiesModal(cap?: CapabilityUI) {
366         let modalConfig: IModalConfig = {
367             size: 'md',
368             title: (cap ? 'Update' : 'Add') + ' Capability',
369             type: 'custom',
370             buttons: [
371                 {
372                     id: 'saveButton',
373                     text: (cap ? 'Update' : 'Create'),
374                     size: "'x-small'",
375                     callback: () => this.createOrUpdateCapability(),
376                     closeModal: true
377                 },
378                 {text: "Cancel", size: "'x-small'", closeModal: true}]
379         };
380         let modalInputs = {
381             capability: cap,
382             capabilityTypesList: this.$scope.capabilityTypesList,
383             isReadonly: this.$scope.isViewMode() || !this.$scope.isDesigner(),
384             validityChangedCallback: this.getDisabled
385         };
386
387         this.ModalServiceSdcUI.openCustomModal(modalConfig, CapabilitiesEditorComponent, {input: modalInputs});
388     }
389
390     getDisabled = (shouldEnable: boolean): void => {
391         let saveButton: ModalButtonComponent = this.ModalServiceSdcUI.getCurrentInstance().getButtonById('saveButton');
392         saveButton.disabled = this.$scope.isViewMode() || !this.$scope.isDesigner() || !shouldEnable;
393     };
394
395     private createOrUpdateRequirement() {
396         let requirement = this.ModalServiceSdcUI.getCurrentInstance().innerModalContent.instance.requirementData;
397         this.$scope.isLoading = true;
398         if (!requirement.uniqueId) {
399             this.ComponentServiceNg2.createRequirement(this.$scope.component, requirement).subscribe(result => {
400                 this.$scope.requirements.unshift(new RequirementUI(result[0], this.$scope.component.uniqueId));
401                 this.$scope.isLoading = false;
402             }, () => {
403                 this.$scope.isLoading = false;
404             });
405         }
406         else {
407             this.ComponentServiceNg2.updateRequirement(this.$scope.component, requirement).subscribe(result => {
408                 let index = this.$scope.requirements.findIndex(req => result[0].uniqueId === req.uniqueId);
409                 this.$scope.requirements[index] = new RequirementUI(result[0], this.$scope.component.uniqueId);
410                 this.$scope.isLoading = false;
411                 this.$scope.$apply();
412             }, () => {
413                 this.$scope.isLoading = false;
414             });
415         }
416     }
417
418     private createOrUpdateCapability() {
419         let capability = this.ModalServiceSdcUI.getCurrentInstance().innerModalContent.instance.capabilityData;
420         this.$scope.isLoading = true;
421         if (!capability.uniqueId) {
422             this.ComponentServiceNg2.createCapability(this.$scope.component, capability).subscribe(result => {
423                 this.$scope.capabilities.unshift(new CapabilityUI(result[0], this.$scope.component.uniqueId));
424                 this.$scope.isLoading = false;
425             }, () => {
426                 this.$scope.isLoading = false;
427             });
428         }
429         else {
430             this.ComponentServiceNg2.updateCapability(this.$scope.component, capability).subscribe(result => {
431                 let index = this.$scope.capabilities.findIndex(cap => result[0].uniqueId === cap.uniqueId);
432                 this.$scope.capabilities[index] = new CapabilityUI(result[0], this.$scope.component.uniqueId);
433                 this.$scope.isLoading = false;
434                 this.$scope.$apply();
435             }, () => {
436                 this.$scope.isLoading = false;
437             });
438         }
439     }
440
441     private deleteRequirement(req) {
442         this.$scope.isLoading = true;
443         this.ComponentServiceNg2.deleteRequirement(this.$scope.component, req.uniqueId).subscribe(() => {
444             this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.componentType, this.$scope.component.uniqueId).subscribe(response => {
445                 this.$scope.component.requirements = response.requirements;
446                 this.initScope('requirements');
447                 this.$scope.isLoading = false;
448             }, () => {
449                 this.$scope.isLoading = false;
450             });
451         }, () => {
452             this.$scope.isLoading = false;
453         });
454     }
455
456     private deleteCapability(cap) {
457         this.$scope.isLoading = true;
458         this.ComponentServiceNg2.deleteCapability(this.$scope.component, cap.uniqueId).subscribe(() => {
459             this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.$scope.componentType, this.$scope.component.uniqueId).subscribe(response => {
460                 this.$scope.component.capabilities = response.capabilities;
461                 this.initScope('capabilities');
462                 this.$scope.isLoading = false;
463             }, () => {
464                 this.$scope.isLoading = false;
465             });
466         }, () => {
467             this.$scope.isLoading = false;
468         });
469     }
470 }
471