Support of get_property for instance properties
[sdc.git] / catalog-ui / src / app / ng2 / pages / properties-assignment / tosca-function / tosca-function.component.ts
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix 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  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  *  SPDX-License-Identifier: Apache-2.0
17  *  ============LICENSE_END=========================================================
18  */
19
20 import {Component, Input} from '@angular/core';
21 import {ComponentMetadata, DataTypeModel, PropertyBEModel, PropertyModel} from 'app/models';
22 import {TopologyTemplateService} from "../../../services/component-services/topology-template.service";
23 import {WorkspaceService} from "../../workspace/workspace.service";
24 import {PropertiesService} from "../../../services/properties.service";
25 import {PROPERTY_DATA} from "../../../../utils/constants";
26 import {DataTypeService} from "../../../services/data-type.service";
27 import {ToscaGetFunctionType} from "../../../../models/tosca-get-function-type";
28 import {TranslateService} from "../../../shared/translator/translate.service";
29 import {ComponentGenericResponse} from '../../../services/responses/component-generic-response';
30 import {Observable} from 'rxjs/Observable';
31 import {PropertySource} from "../../../../models/property-source";
32 import {InstanceFeDetails} from "../../../../models/instance-fe-details";
33 import {ToscaGetFunction} from "../../../../models/tosca-get-function";
34
35 @Component({
36     selector: 'tosca-function',
37     templateUrl: './tosca-function.component.html',
38     styleUrls: ['./tosca-function.component.less'],
39 })
40 export class ToscaFunctionComponent {
41
42     @Input() property: PropertyBEModel;
43     @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();
44
45     TOSCA_FUNCTION_GET_PROPERTY = ToscaGetFunctionType.GET_PROPERTY;
46
47     selectedProperty: PropertyDropdownValue;
48     isLoading: boolean = false;
49     propertyDropdownList: Array<PropertyDropdownValue> = [];
50     toscaFunctions: Array<string> = [];
51     propertySourceList: Array<string> = [];
52     instanceNameAndIdMap: Map<string, string> = new Map<string, string>();
53     dropdownValuesLabel: string;
54     dropDownErrorMsg: string;
55     propertySource: string
56     toscaGetFunction: ToscaGetFunction = new ToscaGetFunction();
57
58     private componentMetadata: ComponentMetadata;
59
60     constructor(private topologyTemplateService: TopologyTemplateService,
61                 private workspaceService: WorkspaceService,
62                 private propertiesService: PropertiesService,
63                 private dataTypeService: DataTypeService,
64                 private translateService: TranslateService) {
65     }
66
67     ngOnInit() {
68         this.componentMetadata = this.workspaceService.metadata;
69         this.loadToscaFunctions();
70         this.loadPropertySourceDropdown();
71     }
72
73     private loadToscaFunctions(): void {
74         this.toscaFunctions.push(ToscaGetFunctionType.GET_INPUT);
75         this.toscaFunctions.push(ToscaGetFunctionType.GET_PROPERTY);
76     }
77
78     private loadPropertySourceDropdown() {
79         this.propertySourceList.push(PropertySource.SELF);
80         this.componentInstanceMap.forEach((value, key) => {
81             const instanceName = value.name;
82             this.instanceNameAndIdMap.set(instanceName, key);
83             if (instanceName !== PropertySource.SELF) {
84                 this.addToPropertySource(instanceName);
85             }
86         });
87     }
88
89     private addToPropertySource(source: string) {
90         this.propertySourceList.push(source);
91         this.propertySourceList.sort((a, b) => {
92             if (a === PropertySource.SELF) {
93                 return -1;
94             } else if (b === PropertySource.SELF) {
95                 return 1;
96             }
97
98             return a.localeCompare(b);
99         });
100     }
101
102     onToscaFunctionChange(): void {
103         this.toscaGetFunction.propertyUniqueId = undefined;
104         this.toscaGetFunction.propertyName = undefined;
105         this.toscaGetFunction.propertySource = undefined;
106         this.toscaGetFunction.sourceUniqueId = undefined;
107         this.toscaGetFunction.sourceName = undefined;
108         this.toscaGetFunction.propertyPathFromSource = undefined;
109         this.propertySource = undefined;
110         if (this.isGetInputSelected()) {
111             this.setSelfPropertySource();
112             this.loadDropdownValueLabel();
113             this.loadDropdownValues();
114         }
115     }
116
117     private loadDropdownValueLabel(): void {
118         if (!this.toscaGetFunction.functionType) {
119             return;
120         }
121         if (this.isGetInputSelected()) {
122             this.dropdownValuesLabel = this.translateService.translate('INPUT_DROPDOWN_LABEL');
123         } else if (this.isGetPropertySelected()) {
124             this.dropdownValuesLabel = this.translateService.translate('TOSCA_FUNCTION_PROPERTY_DROPDOWN_LABEL');
125         }
126     }
127
128     private loadDropdownValues(): void {
129         if (!this.toscaGetFunction.functionType) {
130             return;
131         }
132         this.resetDropDown();
133         this.loadPropertiesInDropdown();
134     }
135
136     private resetDropDown() {
137         this.dropDownErrorMsg = undefined;
138         this.propertyDropdownList = [];
139     }
140
141     private loadPropertiesInDropdown() {
142         this.startLoading();
143         const propertiesObservable: Observable<ComponentGenericResponse> = this.getPropertyObservable();
144         propertiesObservable.subscribe( (response: ComponentGenericResponse) => {
145             const properties: PropertyBEModel[] = this.extractProperties(response);
146             if (!properties || properties.length === 0) {
147                 const msgCode = this.isGetInputSelected() ? 'TOSCA_FUNCTION_NO_INPUT_FOUND' : 'TOSCA_FUNCTION_NO_PROPERTY_FOUND';
148                 this.dropDownErrorMsg = this.translateService.translate(msgCode, {type: this.property.type});
149                 return;
150             }
151             this.addPropertiesToDropdown(properties);
152             if (this.propertyDropdownList.length == 0) {
153                 const msgCode = this.isGetInputSelected() ? 'TOSCA_FUNCTION_NO_INPUT_FOUND' : 'TOSCA_FUNCTION_NO_PROPERTY_FOUND';
154                 this.dropDownErrorMsg = this.translateService.translate(msgCode, {type: this.property.type});
155             }
156         }, (error) => {
157             console.error('An error occurred while loading properties.', error);
158         }, () => {
159             this.stopLoading();
160         });
161     }
162
163     private extractProperties(componentGenericResponse: ComponentGenericResponse): PropertyBEModel[] {
164         if (this.isGetInputSelected()) {
165             return componentGenericResponse.inputs;
166         }
167         if (this.isGetPropertySelected()) {
168             if (this.propertySource === PropertySource.SELF) {
169                 return componentGenericResponse.properties;
170             }
171             const componentInstanceProperties: PropertyModel[] = componentGenericResponse.componentInstancesProperties[this.instanceNameAndIdMap.get(this.propertySource)];
172             return this.removeSelectedProperty(componentInstanceProperties);
173         }
174     }
175
176     private getPropertyObservable(): Observable<ComponentGenericResponse> {
177         if (this.isGetInputSelected()) {
178             return this.topologyTemplateService.getComponentInputsValues(this.componentMetadata.componentType, this.componentMetadata.uniqueId);
179         }
180         if (this.isGetPropertySelected()) {
181             if (this.propertySource === PropertySource.SELF) {
182                 return this.topologyTemplateService.findAllComponentProperties(this.componentMetadata.componentType, this.componentMetadata.uniqueId);
183             }
184             return this.topologyTemplateService.getComponentInstanceProperties(this.componentMetadata.componentType, this.componentMetadata.uniqueId);
185         }
186     }
187
188     private removeSelectedProperty(componentInstanceProperties: PropertyModel[]): PropertyModel[] {
189         if (!componentInstanceProperties) {
190             return [];
191         }
192         return componentInstanceProperties.filter(property =>
193             (property.uniqueId !== this.property.uniqueId) ||
194             (property.uniqueId === this.property.uniqueId && property.resourceInstanceUniqueId !== this.property.parentUniqueId)
195         );
196     }
197
198     private addPropertyToDropdown(propertyDropdownValue: PropertyDropdownValue) {
199         this.propertyDropdownList.push(propertyDropdownValue);
200         this.propertyDropdownList.sort((a, b) => a.propertyLabel.localeCompare(b.propertyLabel));
201     }
202
203     private addPropertiesToDropdown(properties: PropertyBEModel[]) {
204         for (const property of properties) {
205             if (this.property.type === property.type) {
206                 this.addPropertyToDropdown({
207                     propertyName: property.name,
208                     propertyId: property.uniqueId,
209                     propertyLabel: property.name,
210                     propertyPath: [property.name]
211                 });
212             } else if (this.isComplexType(property.type)) {
213                 this.fillPropertyDropdownWithMatchingChildProperties(property);
214             }
215         }
216     }
217
218     private fillPropertyDropdownWithMatchingChildProperties(inputProperty: PropertyBEModel, parentPropertyList: Array<PropertyBEModel> = []) {
219         const dataTypeFound: DataTypeModel = this.dataTypeService.getDataTypeByModelAndTypeName(this.componentMetadata.model, inputProperty.type);
220         if (!dataTypeFound || !dataTypeFound.properties) {
221             return;
222         }
223         parentPropertyList.push(inputProperty);
224         dataTypeFound.properties.forEach(dataTypeProperty => {
225             if (dataTypeProperty.type === this.property.type) {
226                 this.addPropertyToDropdown({
227                     propertyName: dataTypeProperty.name,
228                     propertyId: parentPropertyList[0].uniqueId,
229                     propertyLabel: parentPropertyList.map(property => property.name).join('->') + '->' + dataTypeProperty.name,
230                     propertyPath: [...parentPropertyList.map(property => property.name), dataTypeProperty.name]
231                 });
232             } else if (PROPERTY_DATA.SIMPLE_TYPES.indexOf(dataTypeProperty.type) === -1) {
233                 this.fillPropertyDropdownWithMatchingChildProperties(dataTypeProperty, [...parentPropertyList])
234             }
235         });
236     }
237
238     private isGetPropertySelected() {
239         return this.toscaGetFunction.functionType === ToscaGetFunctionType.GET_PROPERTY;
240     }
241
242     private isGetInputSelected() {
243         return this.toscaGetFunction.functionType === ToscaGetFunctionType.GET_INPUT;
244     }
245
246     private isComplexType(propertyType: string) {
247         return PROPERTY_DATA.SIMPLE_TYPES.indexOf(propertyType) === -1;
248     }
249
250     private stopLoading() {
251         this.isLoading = false;
252     }
253
254     private startLoading() {
255         this.isLoading = true;
256     }
257
258     showDropdown(): boolean {
259         if (this.toscaGetFunction.functionType === ToscaGetFunctionType.GET_PROPERTY) {
260             return this.toscaGetFunction.propertySource && !this.isLoading && !this.dropDownErrorMsg;
261         }
262
263         return this.toscaGetFunction.functionType && !this.isLoading && !this.dropDownErrorMsg;
264     }
265
266     onPropertySourceChange() {
267         if (!this.toscaGetFunction.functionType || !this.propertySource) {
268             return;
269         }
270         this.toscaGetFunction.propertyUniqueId = undefined;
271         this.toscaGetFunction.propertyName = undefined;
272         this.toscaGetFunction.propertyPathFromSource = undefined;
273         if (this.propertySource === PropertySource.SELF) {
274             this.setSelfPropertySource();
275         } else {
276             this.toscaGetFunction.propertySource = PropertySource.INSTANCE;
277             this.toscaGetFunction.sourceName = this.propertySource;
278             this.toscaGetFunction.sourceUniqueId = this.instanceNameAndIdMap.get(this.propertySource);
279         }
280         this.loadDropdownValueLabel();
281         this.resetDropDown();
282         this.loadPropertiesInDropdown();
283     }
284
285     private setSelfPropertySource() {
286         this.toscaGetFunction.propertySource = PropertySource.SELF;
287         this.toscaGetFunction.sourceName = this.componentMetadata.name;
288         this.toscaGetFunction.sourceUniqueId = this.componentMetadata.uniqueId;
289     }
290
291     onPropertyChange() {
292         this.toscaGetFunction.propertyUniqueId = this.selectedProperty.propertyId;
293         this.toscaGetFunction.propertyName = this.selectedProperty.propertyName;
294         this.toscaGetFunction.propertyPathFromSource = this.selectedProperty.propertyPath;
295     }
296
297 }
298
299 export interface PropertyDropdownValue {
300     propertyName: string;
301     propertyId: string;
302     propertyLabel: string;
303     propertyPath: Array<string>;
304 }