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