66ae3cc23c59a19d46c8a15557a1cd7877d833bd
[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, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges} from '@angular/core';
21 import {ComponentMetadata, PropertyBEModel, PropertyDeclareAPIModel, DerivedFEProperty} from 'app/models';
22 import {TopologyTemplateService} from "../../../services/component-services/topology-template.service";
23 import {WorkspaceService} from "../../workspace/workspace.service";
24 import {ToscaGetFunctionType} from "../../../../models/tosca-get-function-type";
25 import {InstanceFeDetails} from "../../../../models/instance-fe-details";
26 import {ToscaGetFunction} from "../../../../models/tosca-get-function";
27 import {FormControl, FormGroup, Validators} from "@angular/forms";
28 import {ToscaFunctionType} from "../../../../models/tosca-function-type.enum";
29 import {ToscaGetFunctionValidationEvent} from "./tosca-get-function/tosca-get-function.component";
30 import {ToscaFunction} from "../../../../models/tosca-function";
31 import {ToscaConcatFunctionValidationEvent} from "./tosca-concat-function/tosca-concat-function.component";
32 import {ToscaCustomFunctionValidationEvent} from "./tosca-custom-function/tosca-custom-function.component";
33 import {PROPERTY_TYPES} from "../../../../utils/constants";
34 import {YamlFunctionValidationEvent} from "./yaml-function/yaml-function.component";
35 import {ToscaConcatFunction} from "../../../../models/tosca-concat-function";
36 import {ToscaCustomFunction} from "../../../../models/tosca-custom-function";
37 import {YamlFunction} from "../../../../models/yaml-function";
38 import {CustomToscaFunction} from "../../../../models/default-custom-functions";
39
40 @Component({
41     selector: 'tosca-function',
42     templateUrl: './tosca-function.component.html',
43     styleUrls: ['./tosca-function.component.less'],
44 })
45 export class ToscaFunctionComponent implements OnInit, OnChanges {
46
47     @Input() property: PropertyBEModel;
48     @Input() overridingType: PROPERTY_TYPES;
49     @Input() inToscaFunction: ToscaFunction;
50     @Input() componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();
51     @Input() customToscaFunctions: Array<CustomToscaFunction> = [];
52     @Input() allowClear: boolean = true;
53     @Input() compositionMap: boolean = false;
54     @Input() compositionMapKey: string = "";
55     @Input() complexListKey: string = null;
56     @Output() onValidFunction: EventEmitter<ToscaGetFunction> = new EventEmitter<ToscaGetFunction>();
57     @Output() onValidityChange: EventEmitter<ToscaFunctionValidationEvent> = new EventEmitter<ToscaFunctionValidationEvent>();
58
59     toscaFunctionForm: FormControl = new FormControl(undefined, [Validators.required]);
60     toscaFunctionTypeForm: FormControl = new FormControl(undefined, Validators.required);
61     formGroup: FormGroup = new FormGroup({
62         'toscaFunction': this.toscaFunctionForm,
63         'toscaFunctionType': this.toscaFunctionTypeForm,
64     });
65
66     isLoading: boolean = false;
67     toscaFunction: ToscaFunction;
68     toscaFunctions: Array<string> = [];
69     toscaCustomFunctions: Array<String> = [];
70
71     private isInitialized: boolean = false;
72     private componentMetadata: ComponentMetadata;
73
74     constructor(private topologyTemplateService: TopologyTemplateService,
75                 private workspaceService: WorkspaceService) {
76     }
77
78     ngOnInit(): void {
79         this.componentMetadata = this.workspaceService.metadata;
80         this.toscaFunction = this.inToscaFunction ? this.inToscaFunction : this.property.toscaFunction ? this.property.toscaFunction : undefined;
81         this.loadToscaFunctions();
82         this.formGroup.valueChanges.subscribe(() => {
83             if (!this.isInitialized) {
84                 return;
85             }
86             if (this.formGroup.valid) {
87                 this.onValidFunction.emit(this.toscaFunctionForm.value);
88             }
89         });
90         this.initToscaFunction();
91         this.emitValidityChange();
92         this.isInitialized = true;
93     }
94
95     ngOnChanges(changes: SimpleChanges): void {
96         if (changes.property) {
97             this.resetForm();
98             this.toscaFunction = this.inToscaFunction ? this.inToscaFunction : this.property.toscaFunction ? this.property.toscaFunction : undefined;
99             this.initToscaFunction();
100             this.loadToscaFunctions();
101             this.emitValidityChange();
102         }
103     }
104
105     private validate(): boolean {
106         return (!this.toscaFunctionForm.value && !this.toscaFunctionTypeForm.value) || this.formGroup.valid;
107     }
108
109     private initToscaFunction(): void {
110         if (this.compositionMap && this.property.subPropertyToscaFunctions) {
111             let keyToFind = [this.compositionMapKey];
112             if (this.complexListKey != null) {
113                 keyToFind = [this.complexListKey,this.compositionMapKey];
114             }       
115             let subPropertyToscaFunction;
116             this.property.subPropertyToscaFunctions.forEach(subToscaFunction => {
117                 if (subToscaFunction.subPropertyPath.toString() == keyToFind.toString()) {
118                     subPropertyToscaFunction = subToscaFunction;
119                 }
120             });
121
122             if (subPropertyToscaFunction){
123                 this.toscaFunction = subPropertyToscaFunction.toscaFunction;
124                 this.toscaFunctionForm.setValue(this.toscaFunction);
125                 let type = this.toscaFunction.type;
126                 if (type == ToscaFunctionType.CUSTOM) {
127                     let name = (subPropertyToscaFunction.toscaFunction as ToscaCustomFunction).name;
128                     let customToscaFunc = this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, name))
129                     if (customToscaFunc) {
130                         this.toscaFunctionTypeForm.setValue(name);
131                     } else {
132                         this.toscaFunctionTypeForm.setValue("other");
133                     }
134                 } else {
135                     this.toscaFunctionTypeForm.setValue(type);
136                 }
137             }
138             return;
139         }
140             if (this.property instanceof PropertyDeclareAPIModel && this.property.subPropertyToscaFunctions && (<PropertyDeclareAPIModel> this.property).propertiesName){
141                 let propertiesPath = (<PropertyDeclareAPIModel> this.property).propertiesName.split("#");
142             if (propertiesPath.length > 1){
143                 let keyToFind = (<DerivedFEProperty>this.property.input).toscaPath;
144                 let subPropertyToscaFunction = this.property.subPropertyToscaFunctions.find(subPropertyToscaFunction => this.areEqual(subPropertyToscaFunction.subPropertyPath, keyToFind.length > 0 ? keyToFind : propertiesPath.slice(1)));
145
146                 if (subPropertyToscaFunction){
147                         this.toscaFunction = subPropertyToscaFunction.toscaFunction;
148                     this.toscaFunctionForm.setValue(this.toscaFunction);
149                     let type = this.toscaFunction.type;
150                     if (type == ToscaFunctionType.CUSTOM) {
151                         let name = (subPropertyToscaFunction.toscaFunction as ToscaCustomFunction).name;
152                         let customToscaFunc = this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, name))
153                         if (customToscaFunc) {
154                             this.toscaFunctionTypeForm.setValue(name);
155                         } else {
156                             this.toscaFunctionTypeForm.setValue("other");
157                         }
158                     } else {
159                         this.toscaFunctionTypeForm.setValue(type);
160                     }
161                 }
162                 return;
163             }
164         }
165         if (!this.property.isToscaFunction()) {
166             return;
167         }
168
169         this.toscaFunctionForm.setValue(this.inToscaFunction ? this.inToscaFunction : this.property.toscaFunction);
170         let type = this.property.toscaFunction.type;
171         if (type == ToscaFunctionType.CUSTOM) {
172             let name = (this.property.toscaFunction as ToscaCustomFunction).name;
173             let customToscaFunc = this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, name))
174             if (customToscaFunc) {
175                 this.toscaFunctionTypeForm.setValue(name);
176             } else {
177                 this.toscaFunctionTypeForm.setValue("other");
178             }
179         } else {
180             this.toscaFunctionTypeForm.setValue(this.inToscaFunction ? this.inToscaFunction.type : type);
181         }
182     }
183
184     private areEqual(array1: string[], array2: string[]): boolean {
185             return array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
186     }
187
188     private loadToscaFunctions(): void {
189         this.toscaFunctions = [];
190         this.toscaFunctions.push(ToscaFunctionType.GET_ATTRIBUTE);
191         this.toscaFunctions.push(ToscaFunctionType.GET_INPUT);
192         this.toscaFunctions.push(ToscaFunctionType.GET_PROPERTY);
193         if ((this.property.type === PROPERTY_TYPES.STRING || this.property.type === PROPERTY_TYPES.ANY) && this.overridingType === undefined) {
194             this.toscaFunctions.push(ToscaFunctionType.CONCAT);
195         }
196         this.loadCustomToscaFunctions();
197     }
198
199     private loadCustomToscaFunctions(): void {
200         if (!this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, "other"))) {
201             let other = new CustomToscaFunction();
202             other.name = "other";
203             other.type = ToscaFunctionType.CUSTOM;
204             this.customToscaFunctions.push(other);
205         }
206         this.toscaCustomFunctions = [];
207         for (let func of this.customToscaFunctions) {
208             this.toscaCustomFunctions.push(func.name);
209         }
210     }
211
212     getCustomToscaFunction(): CustomToscaFunction {
213         let funcName = this.formGroup.get('toscaFunctionType').value;
214         return this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, funcName));
215     }
216
217     getCustomFunctionName():string {
218         let toscaFunctionType: CustomToscaFunction = this.getCustomToscaFunction();
219         let name = toscaFunctionType.name;
220         return name == 'other' ? '' : name;
221     }
222
223     getCustomFunctionType():string {
224         let toscaFunctionType: CustomToscaFunction = this.getCustomToscaFunction();
225         return toscaFunctionType.type;
226     }
227
228     isDefaultCustomFunction(): boolean {
229         let toscaFunctionType: CustomToscaFunction = this.getCustomToscaFunction();
230         if (toscaFunctionType.name === "other") {
231             return false;
232         }
233         return this.customToscaFunctions.filter(e => e.name === toscaFunctionType.name).length > 0;
234     }
235
236     private resetForm(): void {
237         this.formGroup.reset();
238         this.toscaFunction = undefined;
239     }
240
241     private isGetPropertySelected(): boolean {
242         return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_PROPERTY;
243     }
244
245     private isGetAttributeSelected(): boolean {
246         return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_ATTRIBUTE;
247     }
248
249     private isGetInputSelected(): boolean {
250         return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_INPUT;
251     }
252
253     isConcatSelected(): boolean {
254         return this.formGroup.get('toscaFunctionType').value === ToscaFunctionType.CONCAT;
255     }
256
257     isCustomSelected(): boolean {
258         let toscaFunctionType: CustomToscaFunction = this.getCustomToscaFunction();
259         return toscaFunctionType && (toscaFunctionType.type === ToscaFunctionType.CUSTOM || toscaFunctionType.type === ToscaFunctionType.GET_INPUT);
260     }
261
262     isGetFunctionSelected(): boolean {
263         return this.isGetInputSelected() || this.isGetPropertySelected() || this.isGetAttributeSelected();
264     }
265
266     isYamlFunctionSelected(): boolean {
267         return this.formGroup.get('toscaFunctionType').value === ToscaFunctionType.YAML;
268     }
269
270     onClearValues(): void {
271         this.resetForm();
272     }
273
274     showClearButton(): boolean {
275         return this.allowClear && this.toscaFunctionTypeForm.value;
276     }
277
278     onConcatFunctionValidityChange(validationEvent: ToscaConcatFunctionValidationEvent): void {
279         if (validationEvent.isValid) {
280             this.toscaFunctionForm.setValue(validationEvent.toscaConcatFunction);
281         } else {
282             this.toscaFunctionForm.setValue(undefined);
283         }
284         this.emitValidityChange();
285     }
286
287     onCustomFunctionValidityChange(validationEvent: ToscaCustomFunctionValidationEvent): void {
288         if (validationEvent.isValid) {
289             this.toscaFunctionForm.setValue(validationEvent.toscaCustomFunction);
290         } else {
291             this.toscaFunctionForm.setValue(undefined);
292         }
293         this.emitValidityChange();
294     }
295
296     onGetFunctionValidityChange(validationEvent: ToscaGetFunctionValidationEvent): void {
297         if (validationEvent.isValid) {
298             this.toscaFunctionForm.setValue(validationEvent.toscaGetFunction);
299         } else {
300             this.toscaFunctionForm.setValue(undefined);
301         }
302         this.emitValidityChange();
303     }
304
305     onYamlFunctionValidityChange(validationEvent: YamlFunctionValidationEvent): void {
306         if (validationEvent.isValid) {
307             this.toscaFunctionForm.setValue(validationEvent.value);
308         } else {
309             this.toscaFunctionForm.setValue(undefined);
310         }
311         this.emitValidityChange();
312     }
313
314     onFunctionTypeChange(): void {
315         this.toscaFunction = undefined;
316         this.toscaFunctionForm.reset();
317     }
318
319     private emitValidityChange(): void {
320         const isValid: boolean = this.validate();
321         this.onValidityChange.emit({
322             isValid: isValid,
323             toscaFunction: isValid ? this.buildFunctionFromForm() : undefined
324         });
325     }
326
327     private buildFunctionFromForm(): ToscaFunction {
328         if (!this.toscaFunctionTypeForm.value) {
329             return undefined;
330         }
331         if (this.isConcatSelected()) {
332             return new ToscaConcatFunction(this.toscaFunctionForm.value);
333         }
334         if (this.isCustomSelected()) {
335             return new ToscaCustomFunction(this.toscaFunctionForm.value);
336         }
337         if (this.isGetFunctionSelected()) {
338             return new ToscaGetFunction(this.toscaFunctionForm.value);
339         }
340         if (this.isYamlFunctionSelected()) {
341             return new YamlFunction(this.toscaFunctionForm.value);
342         }
343
344         console.error(`Function ${this.toscaFunctionTypeForm.value} not supported`);
345     }
346 }
347
348 export class ToscaFunctionValidationEvent {
349     isValid: boolean;
350     toscaFunction: ToscaFunction;
351 }