Support complex types in artifact properties
[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     artifactTypeProperties: Array<InputOperationParameter> = [];
70
71     toscaArtifactTypes: Array<DropdownValue> = [];
72
73     enableAddArtifactImplementation: boolean;
74     propertyValueValid: boolean = true;
75     inputTypeOptions: any[];
76
77     constructor(private dataTypeService: DataTypeService) {
78         this.dataTypeMap$ = new Observable<Map<string, DataTypeModel>>(subscriber => {
79             this.dataTypeService.findAllDataTypesByModel(this.modelName)
80             .then((dataTypesMap: Map<string, DataTypeModel>) => {
81                 subscriber.next(dataTypesMap);
82             });
83         });
84         this.dataTypeMap$.subscribe(value => {
85             this.dataTypeMap = value;
86         });
87
88     }
89
90     ngOnInit() {
91         this.isViewOnly = this.input.isViewOnly;
92         this.interfaceType = this.input.selectedInterface.displayType();
93         this.operationToUpdate = this.input.selectedInterfaceOperation;
94         this.operationToUpdate.interfaceId = this.input.selectedInterface.uniqueId;
95         this.operationToUpdate.interfaceType = this.input.selectedInterface.type;
96         this.initInputs();
97         this.removeImplementationQuote();
98         this.validityChanged();
99         this.loadInterfaceOperationImplementation();
100     }
101
102     private initInputs() {
103         if (!this.operationToUpdate.inputs) {
104             this.operationToUpdate.inputs = new class implements IOperationParamsList {
105                 listToscaDataDefinition: Array<InputOperationParameter> = [];
106             }
107         }
108         this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
109     }
110
111     private loadInterfaceOperationImplementation() {
112         this.toscaArtifactTypes = this.input.toscaArtifactTypes;
113         this.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
114         this.artifactName = this.operationToUpdate.implementation.artifactName;
115         this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
116         this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
117         this.getArtifactTypesSelected();
118     }
119
120     onDescriptionChange= (value: any): void => {
121         this.operationToUpdate.description = value;
122     }
123
124     onImplementationNameChange(value: any) {
125         this.readonly = true
126         if (value) {
127             let artifact = new ArtifactModel();
128             artifact.artifactName = value;
129             this.operationToUpdate.implementation = artifact;
130             this.enableAddArtifactImplementation = false;
131             this.readonly = false;
132         }
133     }
134
135     onPropertyValueChange = (propertyValue) => {
136         this.emitter.emit(propertyValue);
137     }
138
139     onMarkToAddArtifactToImplementation(event: any) {
140         if (!event) {
141             this.toscaArtifactTypeSelected = undefined;
142             this.artifactVersion = undefined;
143             if (this.operationToUpdate.implementation.artifactType) {
144                 this.artifactName = undefined;
145             }
146             this.toscaArtifactTypeProperties = undefined;
147             this.artifactTypeProperties = undefined;
148         } else {
149             this.getArtifactTypesSelected();
150         }
151         this.enableAddArtifactImplementation = event;
152         this.validateRequiredField();
153     }
154
155     onSelectToscaArtifactType(type: IDropDownOption) {
156         if (type) {
157             let toscaArtifactType = type.value;
158             let artifact = new ArtifactModel();
159             this.artifactName = undefined;
160             this.artifactVersion = undefined;
161             artifact.artifactType = toscaArtifactType.type;
162             artifact.properties = toscaArtifactType.properties;
163             this.toscaArtifactTypeProperties = artifact.properties;
164             this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
165             this.toscaArtifactTypeSelected = artifact.artifactType;
166             this.operationToUpdate.implementation = artifact;
167             this.getArtifactTypesSelected();
168         }
169         this.validateRequiredField();
170     }
171
172     onArtifactFileChange(value: any) {
173         if (value) {
174             this.operationToUpdate.implementation.artifactName = value;
175         }
176         this.validateRequiredField();
177     }
178
179     onArtifactVersionChange(value: any) {
180         if (value) {
181             this.operationToUpdate.implementation.artifactVersion = value;
182         }
183     }
184
185     onAddInput(inputOperationParameter: InputOperationParameter) {
186         this.addInput(inputOperationParameter);
187         this.validityChanged();
188     }
189
190     propertyValueValidation = (propertyValue): void => {
191         this.onPropertyValueChange(propertyValue);
192         this.propertyValueValid = propertyValue.isValid;
193         this.readonly = !this.propertyValueValid;
194         this.validateRequiredField();
195     }
196
197     onRemoveInput = (inputParam: InputOperationParameter): void => {
198         let index = this.inputs.indexOf(inputParam);
199         this.inputs.splice(index, 1);
200         this.validityChanged();
201     }
202
203     private removeImplementationQuote(): void {
204         if (this.operationToUpdate.implementation) {
205             if (!this.operationToUpdate.implementation
206                 || !this.operationToUpdate.implementation.artifactName) {
207                 return;
208             }
209
210             let implementation = this.operationToUpdate.implementation.artifactName.trim();
211
212             if (implementation.startsWith("'") && implementation.endsWith("'")) {
213                 this.operationToUpdate.implementation.artifactName = implementation.slice(1, -1);
214             }
215         }
216     }
217
218     validityChanged = () => {
219         let validState = this.checkFormValidForSubmit();
220         this.input.validityChangedCallback(validState);
221         if (validState) {
222             this.readonly = false;
223         }
224     }
225
226     private getArtifactTypesSelected() {
227         if (this.operationToUpdate.implementation && this.operationToUpdate.implementation.artifactType) {
228             this.artifactName = this.operationToUpdate.implementation.artifactName;
229             this.toscaArtifactTypeSelected = this.operationToUpdate.implementation.artifactType;
230             this.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
231             this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
232             this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
233             this.enableAddArtifactImplementation = true;
234         }
235         this.validateRequiredField();
236     }
237
238     validateRequiredField = () => {
239         this.readonly = true;
240         const isRequiredFieldSelected = this.isRequiredFieldsSelected();
241         this.input.validityChangedCallback(isRequiredFieldSelected);
242         if (isRequiredFieldSelected && this.propertyValueValid) {
243             this.readonly = false;
244         }
245     }
246
247     private isRequiredFieldsSelected() {
248         return this.toscaArtifactTypeSelected && this.artifactName;
249     }
250
251     private checkFormValidForSubmit = (): boolean => {
252         return this.operationToUpdate.name && this.artifactName && this.isParamsValid();
253     }
254
255     private isParamsValid = (): boolean => {
256         const isInputValid = (input) => input.name && input.inputId && input.type;
257         const isValid = this.inputs.every(isInputValid);
258         if (!isValid) {
259             this.readonly = true;
260         }
261         return isValid;
262     }
263
264     toDropDownOption(val: string) {
265         return { value : val, label: val };
266     }
267
268     /**
269      * Handles the input value change event.
270      * @param changedInput the changed input
271      */
272     onInputValueChange(changedInput: InputOperationParameter) {
273         if (changedInput.value instanceof Object) {
274             changedInput.value = JSON.stringify(changedInput.value);
275         }
276         const inputOperationParameter = this.inputs.find(value => value.name == changedInput.name);
277         inputOperationParameter.value = changedInput.value;
278     }
279
280     onArtifactPropertyValueChange(changedProperty: InputOperationParameter) {
281         if (changedProperty.value instanceof Object) {
282             changedProperty.value = JSON.stringify(changedProperty.value);
283         }
284         this.toscaArtifactTypeProperties.find(artifactProperty => artifactProperty.name == changedProperty.name);
285         const property = this.toscaArtifactTypeProperties.find(artifactProperty => artifactProperty.name == changedProperty.name);
286         property.value = changedProperty.value;
287     }
288
289     /**
290      * Handles the add input event.
291      * @param input the input to add
292      * @private
293      */
294     private addInput(input: InputOperationParameter) {
295         this.operationToUpdate.inputs.listToscaDataDefinition.push(input);
296         this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
297     }
298
299     /**
300      * Return a list with current input names.
301      */
302     collectInputNames() {
303         return this.inputs.map((input) => input.name);
304     }
305
306     /**
307      * Handles the delete input event.
308      * @param inputName the name of the input to be deleted
309      */
310     onInputDelete(inputName: string) {
311         const currentInputs = this.operationToUpdate.inputs.listToscaDataDefinition;
312         const input1 = currentInputs.find(value => value.name === inputName);
313         const indexOfInput = currentInputs.indexOf(input1);
314         if (indexOfInput === -1) {
315             console.error(`Could delete input '${inputName}'. Input not found.`);
316             return;
317         }
318         currentInputs.splice(currentInputs.indexOf(input1), 1);
319         this.inputs = Array.from(currentInputs);
320     }
321
322     private convertArtifactsPropertiesToInput(): Array<InputOperationParameter> {
323         if (!this.toscaArtifactTypeProperties) {
324             return [];
325         }
326         const inputList: Array<InputOperationParameter> = [];
327         this.toscaArtifactTypeProperties.forEach(property => {
328             const input = new InputOperationParameter();
329             input.name = property.name;
330             input.type = property.type;
331             input.schema = property.schema;
332             input.toscaDefaultValue = property.defaultValue;
333             input.value = property.value;
334             inputList.push(input);
335         });
336         return inputList;
337     }
338 }