Support occurrences on node templates
[sdc.git] / catalog-ui / src / app / ng2 / pages / composition / panel / panel-tabs / properties-tab / properties-tab.component.ts
1 import { Component, Input, OnInit } from '@angular/core';
2 import { Store } from '@ngxs/store';
3 import {
4     AttributeModel,
5     AttributesGroup,
6     Component as TopologyTemplate,
7     ComponentInstance,
8     ComponentMetadata,
9     FullComponentInstance,
10     PropertiesGroup,
11     PropertyModel
12 } from 'app/models';
13 import { CompositionService } from 'app/ng2/pages/composition/composition.service';
14 import { WorkspaceService } from 'app/ng2/pages/workspace/workspace.service';
15 import { GroupByPipe } from 'app/ng2/pipes/groupBy.pipe';
16 import { ResourceNamePipe } from 'app/ng2/pipes/resource-name.pipe';
17 import { TopologyTemplateService } from 'app/ng2/services/component-services/topology-template.service';
18 import { ComponentInstanceServiceNg2 } from "app/ng2/services/component-instance-services/component-instance.service";
19 import { ComponentGenericResponse } from 'app/ng2/services/responses/component-generic-response';
20 import { TranslateService } from 'app/ng2/shared/translator/translate.service';
21 import { ModalsHandler } from 'app/utils';
22 import { SdcUiCommon, SdcUiComponents, SdcUiServices } from 'onap-ui-angular';
23 import {SelectedComponentType, TogglePanelLoadingAction} from "../../../common/store/graph.actions";
24
25 @Component({
26     selector: 'properties-tab',
27     templateUrl: './properties-tab.component.html',
28     styleUrls: ['./properties-tab.component.less']
29 })
30 export class PropertiesTabComponent implements OnInit {
31     attributes: AttributesGroup;
32     isComponentInstanceSelected: boolean;
33     properties: PropertiesGroup;
34     groupPropertiesByInstance: boolean;
35     propertiesMessage: string;
36     metadata: ComponentMetadata;
37     objectKeys = Object.keys;
38     isUnboundedChecked: boolean;
39     isOccurrencesEnabled: boolean = false;
40     isLoading: boolean;
41
42     @Input() isViewOnly: boolean;
43     @Input() componentType: SelectedComponentType;
44     @Input() component: FullComponentInstance | TopologyTemplate;
45     @Input() input: {title: string};
46
47     constructor(private store: Store,
48                 private workspaceService: WorkspaceService,
49                 private compositionService: CompositionService,
50                 private modalsHandler: ModalsHandler,
51                 private topologyTemplateService: TopologyTemplateService,
52                 private componentInstanceService: ComponentInstanceServiceNg2,
53                 private modalService: SdcUiServices.ModalService,
54                 private translateService: TranslateService,
55                 private groupByPipe: GroupByPipe) {
56     }
57
58     ngOnInit() {
59         this.metadata = this.workspaceService.metadata;
60         this.isComponentInstanceSelected = this.componentType === SelectedComponentType.COMPONENT_INSTANCE;
61         this.getComponentInstancesPropertiesAndAttributes();
62     }
63
64     public isPropertyOwner = (): boolean => {
65         return this.component instanceof TopologyTemplate && this.component.isResource();
66     }
67
68     public updateProperty = (property: PropertyModel): void => {
69         this.openEditPropertyModal(property);
70     }
71
72     public deleteProperty = (property: PropertyModel): void => {
73
74         const onOk: Function = (): void => {
75             this.store.dispatch(new TogglePanelLoadingAction({isLoading: true}));
76             this.topologyTemplateService.deleteProperty(this.component.componentType, this.component.uniqueId, property.uniqueId)
77                 .subscribe((response) => {
78                     this.store.dispatch(new TogglePanelLoadingAction({isLoading: false}));
79                     this.component.properties = this.component.properties.filter((prop) => prop.uniqueId !==  property.uniqueId);
80                     this.initComponentProperties();
81                 }, () => {
82                     this.store.dispatch(new TogglePanelLoadingAction({isLoading: false}));
83                 });
84         };
85
86         const title: string = this.translateService.translate('PROPERTY_VIEW_DELETE_MODAL_TITLE');
87         const message: string = this.translateService.translate('PROPERTY_VIEW_DELETE_MODAL_TEXT', {name: property.name});
88         const okButton = {
89             testId: 'OK',
90             text: 'OK',
91             type: SdcUiCommon.ButtonType.info,
92             callback: onOk,
93             closeModal: true} as SdcUiComponents.ModalButtonComponent;
94         this.modalService.openInfoModal(title, message, 'delete-modal', [okButton]);
95     }
96
97     public groupNameByKey = (key: string): string => {
98         switch (key) {
99             case 'derived':
100                 return 'Derived';
101
102             case this.metadata.uniqueId:
103                 return ResourceNamePipe.getDisplayName(this.metadata.name);
104
105             default:
106                 return this.getComponentInstanceNameFromInstanceByKey(key);
107         }
108     }
109
110     public getComponentInstanceNameFromInstanceByKey = (key: string): string => {
111         let instanceName: string = '';
112         const componentInstance = this.compositionService.getComponentInstances().find((item) => item.uniqueId === key);
113         if (key !== undefined && componentInstance) {
114
115             instanceName = ResourceNamePipe.getDisplayName(componentInstance.name);
116         }
117         return instanceName;
118     }
119
120     private getComponentInstancesPropertiesAndAttributes = () => {
121         this.topologyTemplateService.getComponentInstanceAttributesAndProperties(
122             this.workspaceService.metadata.uniqueId,
123             this.workspaceService.metadata.componentType)
124             .subscribe((genericResponse: ComponentGenericResponse) => {
125                 this.compositionService.componentInstancesAttributes = genericResponse.componentInstancesAttributes || new AttributesGroup();
126                 this.compositionService.componentInstancesProperties = genericResponse.componentInstancesProperties;
127                 this.initPropertiesAndAttributes();
128             });
129     }
130
131     private initComponentProperties = (): void => {
132         let result: PropertiesGroup = {};
133
134         this.propertiesMessage = undefined;
135         this.groupPropertiesByInstance = false;
136         if (this.component instanceof FullComponentInstance) {
137             result[this.component.uniqueId] = _.orderBy(this.compositionService.componentInstancesProperties[this.component.uniqueId], ['name']);
138             if (this.component.originType === 'VF') {
139                 this.groupPropertiesByInstance = true;
140                 result[this.component.uniqueId] = Array.from(this.groupByPipe.transform(result[this.component.uniqueId], 'path'));
141             }
142         } else if (this.metadata.isService()) {
143             // Temporally fix to hide properties for service (UI stack when there are many properties)
144             result = this.compositionService.componentInstancesProperties;
145             this.propertiesMessage = 'Note: properties for service are disabled';
146         } else {
147             const componentUid = this.component.uniqueId;
148             result[componentUid] = Array<PropertyModel>();
149             const derived = Array<PropertyModel>();
150             _.forEach(this.component.properties, (property: PropertyModel) => {
151                 if (componentUid === property.parentUniqueId) {
152                     result[componentUid].push(property);
153                 } else {
154                     property.readonly = true;
155                     derived.push(property);
156                 }
157             });
158             if (derived.length) {
159                 result['derived'] = derived;
160             }
161             this.objectKeys(result).forEach((key) => { result[key] =  _.orderBy(result[key], ['name']); });
162         }
163         this.properties = result;
164     }
165
166     private initComponentAttributes = (): void => {
167         let result: AttributesGroup = {};
168
169         if (this.component) {
170             if (this.component instanceof FullComponentInstance) {
171                 result[this.component.uniqueId] = this.compositionService.componentInstancesAttributes[this.component.uniqueId] || [];
172             } else if (this.metadata.isService()) {
173                 result = this.compositionService.componentInstancesAttributes;
174             } else {
175                 result[this.component.uniqueId] = (this.component as TopologyTemplate).attributes;
176             }
177             this.attributes = result;
178             this.objectKeys(this.attributes).forEach((key) => {
179                 this.attributes[key] =  _.orderBy(this.attributes[key], ['name']);
180             });
181
182         }
183     }
184
185     private initComponentOccurrences = (): void => {
186         if (this.component instanceof FullComponentInstance) {
187             if(this.component.minOccurrences != null && this.component.maxOccurrences != null){
188                 this.isOccurrencesEnabled = true;
189             }
190             this.isUnboundedChecked = this.component.maxOccurrences == "UNBOUNDED" ? true: false;
191         }
192     }
193
194     /**
195      * This function is checking if the component is the value owner of the current property
196      * in order to notify the edit property modal which fields to disable
197      */
198     private isPropertyValueOwner = (): boolean => {
199         return this.metadata.isService() || !!this.component;
200     }
201
202     /**
203      *  The function opens the edit property modal.
204      *  It checks if the property is from the VF or from one of it's resource instances and sends the needed property list.
205      *  For create property reasons an empty array is transferd
206      *
207      * @param property the wanted property to edit/create
208      */
209     private openEditPropertyModal = (property: PropertyModel): void => {
210         this.modalsHandler.newOpenEditPropertyModal(property,
211             (this.isPropertyOwner() ?
212                 this.properties[property.parentUniqueId] :
213                 this.properties[property.resourceInstanceUniqueId]) || [],
214             this.isPropertyValueOwner(), 'component', property.resourceInstanceUniqueId).then((updatedProperty: PropertyModel) => {
215                 if (updatedProperty) {
216                     const oldProp = _.find(this.properties[updatedProperty.resourceInstanceUniqueId],
217                                  (prop: PropertyModel) => prop.uniqueId === updatedProperty.uniqueId);
218                     oldProp.value = updatedProperty.value;
219                 }
220         });
221     }
222
223     private initPropertiesAndAttributes = (): void => {
224         this.initComponentProperties();
225         this.initComponentAttributes();
226         this.initComponentOccurrences();
227     }
228
229     onUnboundedChanged(component: ComponentInstance) {
230         this.isUnboundedChecked = !this.isUnboundedChecked;
231         component.maxOccurrences = this.isUnboundedChecked ? "UNBOUNDED" : "1";
232     }
233
234     private updateComponentInstance(component: ComponentInstance) {
235         this.store.dispatch(new TogglePanelLoadingAction({isLoading: true}));
236
237         this.componentInstanceService.updateComponentInstance(this.workspaceService.metadata.componentType,
238                                                               this.workspaceService.metadata.uniqueId, component)
239                                                               .subscribe((updatedComponentInstance: ComponentInstance) => {
240             component = new ComponentInstance(updatedComponentInstance);
241             this.compositionService.getComponentInstances().find((item) => item.uniqueId === component.uniqueId).maxOccurrences = component.maxOccurrences;
242             this.compositionService.getComponentInstances().find((item) => item.uniqueId === component.uniqueId).minOccurrences = component.minOccurrences;
243             this.store.dispatch(new TogglePanelLoadingAction({isLoading: false}));
244         }, (error:any) => {
245             this.store.dispatch(new TogglePanelLoadingAction({isLoading: false}));
246             if (error) {
247                 console.log(error);
248             }});
249     }
250
251     private enableOccurrences = () => {
252         if(this.component instanceof FullComponentInstance){
253             if(!this.isOccurrencesEnabled){
254                 this.component.minOccurrences = null;
255                 this.component.maxOccurrences = null;
256             } else {
257                 this.component.minOccurrences = "1";
258                 this.component.maxOccurrences = "1";
259             }
260             this.updateComponentInstance(this.component);
261         }
262     }
263
264     private isOccurrencesFormValid(component: FullComponentInstance) {
265         if(
266             component.minOccurrences && parseInt(component.minOccurrences) >= 0 &&
267             component.maxOccurrences && (parseInt(component.maxOccurrences) >= parseInt(component.minOccurrences) || component.maxOccurrences === "UNBOUNDED")
268         ) {
269             return true;
270         } else {
271             return false;
272         }
273     }
274
275     private saveOccurrences = () => {
276         if(this.component instanceof FullComponentInstance && this.isOccurrencesFormValid(this.component)) {
277             this.updateComponentInstance(this.component);
278         }
279     }
280 }