UI support for default custom function names
[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, PROPERTY_DATA} 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() componentInstanceMap: Map<string, InstanceFeDetails> = new Map<string, InstanceFeDetails>();
49     @Input() customToscaFunctions: Array<CustomToscaFunction> = [];
50     @Input() allowClear: boolean = true;
51     @Input() compositionMap: boolean = false;
52     @Input() compositionMapKey: string = "";
53     @Output() onValidFunction: EventEmitter<ToscaGetFunction> = new EventEmitter<ToscaGetFunction>();
54     @Output() onValidityChange: EventEmitter<ToscaFunctionValidationEvent> = new EventEmitter<ToscaFunctionValidationEvent>();
55
56     toscaFunctionForm: FormControl = new FormControl(undefined, [Validators.required]);
57     toscaFunctionTypeForm: FormControl = new FormControl(undefined, Validators.required);
58     formGroup: FormGroup = new FormGroup({
59         'toscaFunction': this.toscaFunctionForm,
60         'toscaFunctionType': this.toscaFunctionTypeForm,
61     });
62
63     isLoading: boolean = false;
64     toscaFunction: ToscaFunction;
65     toscaFunctions: Array<string> = [];
66     toscaCustomFunctions: Array<String> = [];
67
68     private isInitialized: boolean = false;
69     private componentMetadata: ComponentMetadata;
70
71     constructor(private topologyTemplateService: TopologyTemplateService,
72                 private workspaceService: WorkspaceService) {
73     }
74
75     ngOnInit(): void {
76         this.componentMetadata = this.workspaceService.metadata;
77         this.toscaFunction = this.property.toscaFunction ? this.property.toscaFunction : undefined;
78         this.loadToscaFunctions();
79         this.formGroup.valueChanges.subscribe(() => {
80             if (!this.isInitialized) {
81                 return;
82             }
83             this.emitValidityChange();
84             if (this.formGroup.valid) {
85                 this.onValidFunction.emit(this.toscaFunctionForm.value);
86             }
87         });
88         this.initToscaFunction();
89         this.emitValidityChange();
90         this.isInitialized = true;
91     }
92
93     ngOnChanges(changes: SimpleChanges): void {
94         if (changes.property) {
95             this.resetForm();
96             this.toscaFunction = this.property.toscaFunction ? this.property.toscaFunction : undefined;
97             this.initToscaFunction();
98             this.loadToscaFunctions();
99             this.emitValidityChange();
100         }
101     }
102
103     private validate(): boolean {
104         return (!this.toscaFunctionForm.value && !this.toscaFunctionTypeForm.value) || this.formGroup.valid;
105     }
106
107     private initToscaFunction(): void {
108         if (this.compositionMap && this.property.subPropertyToscaFunctions) {
109             let keyToFind = [this.compositionMapKey];
110             let subPropertyToscaFunction = this.property.subPropertyToscaFunctions.find(subPropertyToscaFunction => this.areEqual(subPropertyToscaFunction.subPropertyPath, keyToFind));
111
112                 if (subPropertyToscaFunction){
113                         this.toscaFunction = subPropertyToscaFunction.toscaFunction;
114                     this.toscaFunctionForm.setValue(this.toscaFunction);
115                     this.toscaFunctionTypeForm.setValue(this.toscaFunction.type);
116                 }
117                 return;
118         }
119             if (this.property instanceof PropertyDeclareAPIModel && this.property.subPropertyToscaFunctions && (<PropertyDeclareAPIModel> this.property).propertiesName){
120                 let propertiesPath = (<PropertyDeclareAPIModel> this.property).propertiesName.split("#");
121             if (propertiesPath.length > 1){
122                 let keyToFind = (<DerivedFEProperty>this.property.input).toscaPath;
123                 let subPropertyToscaFunction = this.property.subPropertyToscaFunctions.find(subPropertyToscaFunction => this.areEqual(subPropertyToscaFunction.subPropertyPath, keyToFind.length > 0 ? keyToFind : propertiesPath.slice(1)));
124
125                 if (subPropertyToscaFunction){
126                         this.toscaFunction = subPropertyToscaFunction.toscaFunction;
127                     this.toscaFunctionForm.setValue(this.toscaFunction);
128                     this.toscaFunctionTypeForm.setValue(this.toscaFunction.type);
129                 }
130                 return;
131             }
132         }
133
134         if (!this.property.isToscaFunction()) {
135             return;
136         }
137         this.toscaFunctionForm.setValue(this.property.toscaFunction);
138         let type = this.property.toscaFunction.type;
139         if (type == ToscaFunctionType.CUSTOM) {
140             let name = (this.property.toscaFunction as ToscaCustomFunction).name;
141             let test = this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, name))
142             if (test) {
143                 this.toscaFunctionTypeForm.setValue(name);
144             } else {
145                 this.toscaFunctionTypeForm.setValue("other");
146             }
147         } else {
148             this.toscaFunctionTypeForm.setValue(type);
149         }
150     }
151
152     private areEqual(array1: string[], array2: string[]): boolean {
153             return array1.length === array2.length && array1.every(function(value, index) { return value === array2[index]})
154     }
155
156     private loadToscaFunctions(): void {
157         this.toscaFunctions = [];
158         this.toscaFunctions.push(ToscaFunctionType.GET_ATTRIBUTE);
159         this.toscaFunctions.push(ToscaFunctionType.GET_INPUT);
160         this.toscaFunctions.push(ToscaFunctionType.GET_PROPERTY);
161         if (this.property.type === PROPERTY_TYPES.STRING || this.property.type === PROPERTY_TYPES.ANY) {
162             this.toscaFunctions.push(ToscaFunctionType.CONCAT);
163         }
164         this.loadCustomToscaFunctions();
165     }
166
167     private loadCustomToscaFunctions(): void {
168         if (!this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, "other"))) {
169             let other = new CustomToscaFunction();
170             other.name = "other";
171             other.type = ToscaFunctionType.CUSTOM;
172             this.customToscaFunctions.push(other);
173         }
174         this.toscaCustomFunctions = [];
175         for (let func of this.customToscaFunctions) {
176             this.toscaCustomFunctions.push(func.name);
177         }
178     }
179
180     getCustomToscaFunction(): CustomToscaFunction {
181         let funcName = this.formGroup.get('toscaFunctionType').value;
182         return this.customToscaFunctions.find(custToscFunc => _.isEqual(custToscFunc.name, funcName));
183     }
184
185     getCustomFunctionName():string {
186         let toscaFunctionType: CustomToscaFunction = this.getCustomToscaFunction();
187         return toscaFunctionType.name;
188     }
189
190     isDefaultCustomFunction(): boolean {
191         let toscaFunctionType: CustomToscaFunction = this.getCustomToscaFunction();
192         if (toscaFunctionType.name === "other") {
193             return false;
194         }
195         return this.customToscaFunctions.filter(e => e.name === toscaFunctionType.name).length > 0;
196     }
197
198     private resetForm(): void {
199         this.formGroup.reset();
200         this.toscaFunction = undefined;
201     }
202
203     private isGetPropertySelected(): boolean {
204         return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_PROPERTY;
205     }
206
207     private isGetAttributeSelected(): boolean {
208         return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_ATTRIBUTE;
209     }
210
211     private isGetInputSelected(): boolean {
212         return this.formGroup.get('toscaFunctionType').value === ToscaGetFunctionType.GET_INPUT;
213     }
214
215     isConcatSelected(): boolean {
216         return this.formGroup.get('toscaFunctionType').value === ToscaFunctionType.CONCAT;
217     }
218
219     isCustomSelected(): boolean {
220         let toscaFunctionType: CustomToscaFunction = this.getCustomToscaFunction();
221         return toscaFunctionType && toscaFunctionType.type === ToscaFunctionType.CUSTOM;
222     }
223
224     isGetFunctionSelected(): boolean {
225         return this.isGetInputSelected() || this.isGetPropertySelected() || this.isGetAttributeSelected();
226     }
227
228     isYamlFunctionSelected(): boolean {
229         return this.formGroup.get('toscaFunctionType').value === ToscaFunctionType.YAML;
230     }
231
232     onClearValues(): void {
233         this.resetForm();
234     }
235
236     showClearButton(): boolean {
237         return this.allowClear && this.toscaFunctionTypeForm.value;
238     }
239
240     onConcatFunctionValidityChange(validationEvent: ToscaConcatFunctionValidationEvent): void {
241         if (validationEvent.isValid) {
242             this.toscaFunctionForm.setValue(validationEvent.toscaConcatFunction);
243         } else {
244             this.toscaFunctionForm.setValue(undefined);
245         }
246     }
247
248     onCustomFunctionValidityChange(validationEvent: ToscaCustomFunctionValidationEvent): void {
249         if (validationEvent.isValid) {
250             this.toscaFunctionForm.setValue(validationEvent.toscaCustomFunction);
251         } else {
252             this.toscaFunctionForm.setValue(undefined);
253         }
254     }
255
256     onGetFunctionValidityChange(validationEvent: ToscaGetFunctionValidationEvent): void {
257         if (validationEvent.isValid) {
258             this.toscaFunctionForm.setValue(validationEvent.toscaGetFunction);
259         } else {
260             this.toscaFunctionForm.setValue(undefined);
261         }
262     }
263
264     onYamlFunctionValidityChange(validationEvent: YamlFunctionValidationEvent): void {
265         if (validationEvent.isValid) {
266             this.toscaFunctionForm.setValue(validationEvent.value);
267         } else {
268             this.toscaFunctionForm.setValue(undefined);
269         }
270     }
271
272     onFunctionTypeChange(): void {
273         this.toscaFunction = undefined;
274         this.toscaFunctionForm.reset();
275     }
276
277     private emitValidityChange(): void {
278         const isValid: boolean = this.validate();
279         this.onValidityChange.emit({
280             isValid: isValid,
281             toscaFunction: isValid ? this.buildFunctionFromForm() : undefined
282         });
283     }
284
285     private buildFunctionFromForm(): ToscaFunction {
286         if (!this.toscaFunctionTypeForm.value) {
287             return undefined;
288         }
289         if (this.isConcatSelected()) {
290             return new ToscaConcatFunction(this.toscaFunctionForm.value);
291         }
292         if (this.isCustomSelected()) {
293             return new ToscaCustomFunction(this.toscaFunctionForm.value);
294         }
295         if (this.isGetFunctionSelected()) {
296             return new ToscaGetFunction(this.toscaFunctionForm.value);
297         }
298         if (this.isYamlFunctionSelected()) {
299             return new YamlFunction(this.toscaFunctionForm.value);
300         }
301
302         console.error(`Function ${this.toscaFunctionTypeForm.value} not supported`);
303     }
304 }
305
306 export class ToscaFunctionValidationEvent {
307     isValid: boolean;
308     toscaFunction: ToscaFunction;
309 }