Support complex types in interface operation inputs
[sdc.git] / catalog-ui / src / app / ng2 / pages / composition / interface-operatons / operation-creator / interface-operation-handler.component.ts
1 /*
2 * ============LICENSE_START=======================================================
3 * SDC
4 * ================================================================================
5 *  Copyright (C) 2021 Nordix Foundation. All rights reserved.
6 *  ================================================================================
7 *  Licensed under the Apache License, Version 2.0 (the "License");
8 *  you may not use this file except in compliance with the License.
9 *  You may obtain a copy of the License at
10 *
11 *        http://www.apache.org/licenses/LICENSE-2.0
12 *  Unless required by applicable law or agreed to in writing, software
13 *  distributed under the License is distributed on an "AS IS" BASIS,
14 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 *  See the License for the specific language governing permissions and
16 *  limitations under the License.
17 *
18 *  SPDX-License-Identifier: Apache-2.0
19 *  ============LICENSE_END=========================================================
20 */
21
22 import {Component, EventEmitter, Input, Output} from '@angular/core';
23 import {UIInterfaceModel} from "../interface-operations.component";
24 import {InputOperationParameter, InterfaceOperationModel, IOperationParamsList} from "../../../../../models/interfaceOperation";
25 import {TranslateService} from "../../../../shared/translator/translate.service";
26 import {IDropDownOption} from "onap-ui-angular/dist/form-elements/dropdown/dropdown-models";
27 import {DropdownValue} from "../../../../components/ui/form-components/dropdown/ui-element-dropdown.component";
28 import {ArtifactModel} from "../../../../../models/artifacts";
29 import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model";
30 import {PropertyParamRowComponent} from "./property-param-row/property-param-row.component";
31 import {PropertyFEModel} from "../../../../../models/properties-inputs/property-fe-model";
32 import {DataTypeService} from "../../../../services/data-type.service";
33 import {Observable} from "rxjs/Observable";
34 import {DataTypeModel} from "../../../../../models/data-types";
35
36 @Component({
37     selector: 'operation-handler',
38     templateUrl: './interface-operation-handler.component.html',
39     styleUrls: ['./interface-operation-handler.component.less'],
40     providers: [TranslateService]
41 })
42 export class InterfaceOperationHandlerComponent {
43
44     @Input() private modelName: string;
45     @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>();
46     input: {
47         toscaArtifactTypes: Array<DropdownValue>;
48         selectedInterface: UIInterfaceModel;
49         selectedInterfaceOperation: InterfaceOperationModel;
50         validityChangedCallback: Function;
51         isViewOnly: boolean;
52     };
53
54     dataTypeMap$: Observable<Map<string, DataTypeModel>>;
55     dataTypeMap: Map<string, DataTypeModel>;
56     interfaceType: string;
57     artifactVersion: string;
58     artifactName: string;
59     interfaceOperationName: string;
60     operationToUpdate: InterfaceOperationModel;
61     inputs: Array<InputOperationParameter> = [];
62     properties: Array<PropertyParamRowComponent> = [];
63     isLoading: boolean = false;
64     readonly: boolean;
65     isViewOnly: boolean;
66
67     toscaArtifactTypeSelected: string;
68     toscaArtifactTypeProperties: Array<PropertyBEModel> = [];
69
70     toscaArtifactTypes: Array<DropdownValue> = [];
71
72     enableAddArtifactImplementation: boolean;
73     propertyValueValid: boolean = true;
74     inputTypeOptions: any[];
75
76     constructor(private dataTypeService: DataTypeService) {
77         this.dataTypeMap$ = new Observable<Map<string, DataTypeModel>>(subscriber => {
78             this.dataTypeService.findAllDataTypesByModel(this.modelName)
79             .then((dataTypesMap: Map<string, DataTypeModel>) => {
80                 subscriber.next(dataTypesMap);
81             });
82         });
83         this.dataTypeMap$.subscribe(value => {
84             this.dataTypeMap = value;
85         });
86
87     }
88
89     ngOnInit() {
90         this.isViewOnly = this.input.isViewOnly;
91         this.interfaceType = this.input.selectedInterface.displayType();
92         this.operationToUpdate = this.input.selectedInterfaceOperation;
93         this.operationToUpdate.interfaceId = this.input.selectedInterface.uniqueId;
94         this.operationToUpdate.interfaceType = this.input.selectedInterface.type;
95         this.initInputs();
96         this.removeImplementationQuote();
97         this.validityChanged();
98         this.loadInterfaceOperationImplementation();
99     }
100
101     private initInputs() {
102         if (!this.operationToUpdate.inputs) {
103             this.operationToUpdate.inputs = new class implements IOperationParamsList {
104                 listToscaDataDefinition: Array<InputOperationParameter> = [];
105             }
106         }
107         this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
108     }
109
110     private loadInterfaceOperationImplementation() {
111         this.toscaArtifactTypes = this.input.toscaArtifactTypes;
112         this.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
113         this.artifactName = this.operationToUpdate.implementation.artifactName;
114         this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
115         this.getArtifactTypesSelected();
116     }
117
118     onDescriptionChange= (value: any): void => {
119         this.operationToUpdate.description = value;
120     }
121
122     onImplementationNameChange(value: any) {
123         this.readonly = true
124         if (value) {
125             let artifact = new ArtifactModel();
126             artifact.artifactName = value;
127             this.operationToUpdate.implementation = artifact;
128             this.enableAddArtifactImplementation = false;
129             this.readonly = false;
130         }
131     }
132
133     onPropertyValueChange = (propertyValue) => {
134         this.emitter.emit(propertyValue);
135     }
136
137     onMarkToAddArtifactToImplementation(event: any) {
138         if (!event) {
139             this.toscaArtifactTypeSelected = undefined;
140             this.artifactVersion = undefined;
141             if (this.operationToUpdate.implementation.artifactType) {
142                 this.artifactName = undefined;
143             }
144             this.toscaArtifactTypeProperties = undefined;
145         } else {
146             this.getArtifactTypesSelected();
147         }
148         this.enableAddArtifactImplementation = event;
149         this.validateRequiredField();
150     }
151
152     onSelectToscaArtifactType(type: IDropDownOption) {
153         if (type) {
154             let toscaArtifactType = type.value;
155             let artifact = new ArtifactModel();
156             this.artifactName = undefined;
157             this.artifactVersion = undefined;
158             artifact.artifactType = toscaArtifactType.type;
159             artifact.properties = toscaArtifactType.properties;
160             this.toscaArtifactTypeProperties = artifact.properties;
161             this.toscaArtifactTypeSelected = artifact.artifactType;
162             this.operationToUpdate.implementation = artifact;
163             this.getArtifactTypesSelected();
164         }
165         this.validateRequiredField();
166     }
167
168     onArtifactFileChange(value: any) {
169         if (value) {
170             this.operationToUpdate.implementation.artifactName = value;
171         }
172         this.validateRequiredField();
173     }
174
175     onArtifactVersionChange(value: any) {
176         if (value) {
177             this.operationToUpdate.implementation.artifactVersion = value;
178         }
179     }
180
181     onAddInput(inputOperationParameter: InputOperationParameter) {
182         this.addInput(inputOperationParameter);
183         this.validityChanged();
184     }
185
186     propertyValueValidation = (propertyValue): void => {
187         this.onPropertyValueChange(propertyValue);
188         this.propertyValueValid = propertyValue.isValid;
189         this.readonly = !this.propertyValueValid;
190         this.validateRequiredField();
191     }
192
193     onRemoveInput = (inputParam: InputOperationParameter): void => {
194         let index = this.inputs.indexOf(inputParam);
195         this.inputs.splice(index, 1);
196         this.validityChanged();
197     }
198
199     private removeImplementationQuote(): void {
200         if (this.operationToUpdate.implementation) {
201             if (!this.operationToUpdate.implementation
202                 || !this.operationToUpdate.implementation.artifactName) {
203                 return;
204             }
205
206             let implementation = this.operationToUpdate.implementation.artifactName.trim();
207
208             if (implementation.startsWith("'") && implementation.endsWith("'")) {
209                 this.operationToUpdate.implementation.artifactName = implementation.slice(1, -1);
210             }
211         }
212     }
213
214     validityChanged = () => {
215         let validState = this.checkFormValidForSubmit();
216         this.input.validityChangedCallback(validState);
217         if (validState) {
218             this.readonly = false;
219         }
220     }
221
222     private getArtifactTypesSelected() {
223         if (this.operationToUpdate.implementation && this.operationToUpdate.implementation.artifactType) {
224             this.artifactName = this.operationToUpdate.implementation.artifactName;
225             this.toscaArtifactTypeSelected = this.operationToUpdate.implementation.artifactType;
226             this.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
227             this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
228             this.enableAddArtifactImplementation = true;
229         }
230         this.validateRequiredField();
231     }
232
233     validateRequiredField = () => {
234         this.readonly = true;
235         const isRequiredFieldSelected = this.isRequiredFieldsSelected();
236         this.input.validityChangedCallback(isRequiredFieldSelected);
237         if (isRequiredFieldSelected && this.propertyValueValid) {
238             this.readonly = false;
239         }
240     }
241
242     private isRequiredFieldsSelected() {
243         return this.toscaArtifactTypeSelected && this.artifactName;
244     }
245
246     private checkFormValidForSubmit = (): boolean => {
247         return this.operationToUpdate.name && this.artifactName && this.isParamsValid();
248     }
249
250     private isParamsValid = (): boolean => {
251         const isInputValid = (input) => input.name && input.inputId && input.type;
252         const isValid = this.inputs.every(isInputValid);
253         if (!isValid) {
254             this.readonly = true;
255         }
256         return isValid;
257     }
258
259     toDropDownOption(val: string) {
260         return { value : val, label: val };
261     }
262
263     /**
264      * Handles the input value change event.
265      * @param changedInput the changed input
266      */
267     onInputValueChange(changedInput: InputOperationParameter) {
268         if (changedInput.value instanceof Object) {
269             changedInput.value = JSON.stringify(changedInput.value);
270         }
271         const inputOperationParameter = this.inputs.find(value => value.name == changedInput.name);
272         inputOperationParameter.value = changedInput.value;
273     }
274
275     /**
276      * Handles the add input event.
277      * @param input the input to add
278      * @private
279      */
280     private addInput(input: InputOperationParameter) {
281         this.operationToUpdate.inputs.listToscaDataDefinition.push(input);
282         this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
283     }
284
285     /**
286      * Return a list with current input names.
287      */
288     collectInputNames() {
289         return this.inputs.map((input) => input.name);
290     }
291
292     /**
293      * Handles the delete input event.
294      * @param inputName the name of the input to be deleted
295      */
296     onInputDelete(inputName: string) {
297         const currentInputs = this.operationToUpdate.inputs.listToscaDataDefinition;
298         const input1 = currentInputs.find(value => value.name === inputName);
299         const indexOfInput = currentInputs.indexOf(input1);
300         if (indexOfInput === -1) {
301             console.error(`Could delete input '${inputName}'. Input not found.`);
302             return;
303         }
304         currentInputs.splice(currentInputs.indexOf(input1), 1);
305         this.inputs = Array.from(currentInputs);
306     }
307 }