Fix edit operation artifact and data types
[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 import {Component, EventEmitter, Input, Output, ViewChild} from '@angular/core';
22 import {UIInterfaceModel} from "../interface-operations.component";
23 import {InputOperationParameter, InterfaceOperationModel, IOperationParamsList} from "../../../../../models/interfaceOperation";
24 import {TranslateService} from "../../../../shared/translator/translate.service";
25 import {DropdownValue} from "../../../../components/ui/form-components/dropdown/ui-element-dropdown.component";
26 import {ArtifactModel} from "../../../../../models/artifacts";
27 import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model";
28 import {PropertyParamRowComponent} from "./property-param-row/property-param-row.component";
29 import {PropertyFEModel} from "../../../../../models/properties-inputs/property-fe-model";
30 import {IDropDownOption} from 'onap-ui-angular';
31 import {ComponentServiceNg2} from "../../../../services/component-services/component.service";
32 import {DropDownComponent} from "onap-ui-angular/dist/form-elements/dropdown/dropdown.component";
33 import {DataTypeService} from "../../../../services/data-type.service";
34 import {Observable} from "rxjs/Observable";
35 import {DataTypeModel} from "../../../../../models/data-types";
36
37 @Component({
38     selector: 'operation-handler',
39     templateUrl: './interface-operation-handler.component.html',
40     styleUrls: ['./interface-operation-handler.component.less'],
41     providers: [TranslateService]
42 })
43 export class InterfaceOperationHandlerComponent {
44
45     @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>();
46     @ViewChild('interfaceOperationDropDown') interfaceOperationDropDown: DropDownComponent;
47
48     input: {
49         toscaArtifactTypes: Array<DropdownValue>;
50         selectedInterface: UIInterfaceModel;
51         selectedInterfaceOperation: InterfaceOperationModel;
52         validityChangedCallback: Function;
53         isViewOnly: boolean;
54         isEdit: boolean;
55         modelName: string;
56     };
57
58     dataTypeMap$: Observable<Map<string, DataTypeModel>>;
59     dataTypeMap: Map<string, DataTypeModel>;
60     interfaceType: string;
61     artifactVersion: string;
62     artifactName: string;
63     interfaceOperationName: string;
64     operationToUpdate: InterfaceOperationModel;
65     inputs: Array<InputOperationParameter> = [];
66     properties: Array<PropertyParamRowComponent> = [];
67     isLoading: boolean = false;
68     readonly: boolean;
69     isViewOnly: boolean;
70     isEdit: boolean;
71     interfaceTypes: Array<DropdownValue> = [];
72     interfaceTypeOptions: Array<DropDownOption> = [];
73     selectedInterfaceType: DropDownOption = undefined;
74     interfaceOperationMap: Map<string, Array<string>> = new Map<string, Array<string>>();
75     interfaceOperationOptions: Array<DropDownOption> = [];
76     selectedInterfaceOperation: DropDownOption = undefined;
77     modelName: string;
78
79     toscaArtifactTypeSelected: string;
80     toscaArtifactTypeProperties: Array<PropertyBEModel> = [];
81     artifactTypeProperties: Array<InputOperationParameter> = [];
82
83     toscaArtifactTypes: Array<DropdownValue> = [];
84
85     enableAddArtifactImplementation: boolean;
86
87     propertyValueValid: boolean = true;
88     inputTypeOptions: any[];
89
90     constructor(private dataTypeService: DataTypeService, private componentServiceNg2: ComponentServiceNg2) {
91
92
93     }
94
95     ngOnInit() {
96         this.isViewOnly = this.input.isViewOnly;
97         this.isEdit = this.input.isEdit;
98         this.interfaceType = this.input.selectedInterface.type;
99         this.operationToUpdate = this.input.selectedInterfaceOperation;
100         this.operationToUpdate.interfaceId = this.input.selectedInterface.uniqueId;
101         this.operationToUpdate.interfaceType = this.input.selectedInterface.type;
102         this.modelName = this.input.modelName;
103         this.initInputs();
104         this.removeImplementationQuote();
105         this.validityChanged();
106         this.loadInterfaceOperationImplementation();
107
108         this.dataTypeMap$ = new Observable<Map<string, DataTypeModel>>(subscriber => {
109             this.dataTypeService.findAllDataTypesByModel(this.modelName)
110             .then((dataTypesMap: Map<string, DataTypeModel>) => {
111                 subscriber.next(dataTypesMap);
112             });
113         });
114         this.dataTypeMap$.subscribe(value => {
115             this.dataTypeMap = value;
116         });
117     }
118
119     private initInputs() {
120         if (!this.operationToUpdate.inputs) {
121             this.operationToUpdate.inputs = new class implements IOperationParamsList {
122                 listToscaDataDefinition: Array<InputOperationParameter> = [];
123             }
124         }
125
126         this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
127         this.removeImplementationQuote();
128         this.validityChanged();
129         this.loadInterfaceOperationImplementation();
130         this.loadInterfaceType();
131     }
132
133     private loadInterfaceType() {
134         this.componentServiceNg2.getInterfaceTypesByModel(this.modelName)
135         .subscribe(response => {
136             if (response) {
137                 this.interfaceOperationMap = new Map<string, Array<string>>();
138                 for (const interfaceType of Object.keys(response).sort()) {
139                     const operationList = response[interfaceType];
140                     operationList.sort();
141                     this.interfaceOperationMap.set(interfaceType, operationList);
142                     const operationDropDownOption: DropDownOption = new DropDownOption(interfaceType);
143                     this.interfaceTypeOptions.push(operationDropDownOption);
144                     if (this.interfaceType == interfaceType) {
145                         this.selectedInterfaceType = operationDropDownOption;
146                     }
147                 }
148                 this.loadInterfaceTypeOperations();
149             }
150         });
151     }
152
153     loadInterfaceTypeOperations() {
154         this.interfaceOperationOptions = new Array<DropDownOption>();
155         const interfaceOperationList = this.interfaceOperationMap.get(this.interfaceType);
156
157         if (interfaceOperationList) {
158             interfaceOperationList.forEach(operationName => {
159                 const operationOption = new DropDownOption(operationName, operationName);
160                 this.interfaceOperationOptions.push(operationOption);
161                 if (this.operationToUpdate.name == operationName) {
162                     this.selectedInterfaceOperation = operationOption
163                 }
164             });
165         }
166
167         this.interfaceOperationDropDown.allOptions = this.interfaceOperationOptions;
168     }
169
170     private loadInterfaceOperationImplementation() {
171         this.toscaArtifactTypes = this.input.toscaArtifactTypes;
172         if (this.operationToUpdate.implementation) {
173             this.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
174             this.artifactName = this.operationToUpdate.implementation.artifactName;
175             this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
176         }
177         this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
178         this.getArtifactTypesSelected();
179     }
180
181     onDescriptionChange = (value: any): void => {
182         this.operationToUpdate.description = value;
183     }
184
185     onImplementationNameChange(value: any) {
186         this.readonly = true
187         if (value || value === '') {
188             let artifact = new ArtifactModel();
189             artifact.artifactName = value;
190             this.operationToUpdate.implementation = artifact;
191             this.enableAddArtifactImplementation = false;
192             this.readonly = false;
193         }
194     }
195
196     onPropertyValueChange = (propertyValue) => {
197         this.emitter.emit(propertyValue);
198     }
199
200     onMarkToAddArtifactToImplementation(event: any) {
201         if (!event) {
202             this.toscaArtifactTypeSelected = undefined;
203             this.artifactVersion = undefined;
204             if (this.operationToUpdate.implementation.artifactType) {
205                 this.operationToUpdate.implementation.artifactName = '';
206                 this.artifactName = undefined;
207             }
208             this.toscaArtifactTypeProperties = undefined;
209             this.artifactTypeProperties = undefined;
210         } else {
211             this.getArtifactTypesSelected();
212         }
213         this.enableAddArtifactImplementation = event;
214         this.validateRequiredField();
215     }
216
217     onSelectToscaArtifactType(type: IDropDownOption) {
218         if (type) {
219             let toscaArtifactType = type.value;
220             let artifact = new ArtifactModel();
221             this.artifactName = undefined;
222             this.artifactVersion = undefined;
223             artifact.artifactType = toscaArtifactType.type;
224             artifact.properties = toscaArtifactType.properties;
225             this.toscaArtifactTypeProperties = artifact.properties;
226             this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
227             this.toscaArtifactTypeSelected = artifact.artifactType;
228             this.operationToUpdate.implementation = artifact;
229             this.getArtifactTypesSelected();
230         }
231         this.validateRequiredField();
232     }
233
234     onArtifactFileChange(value: any) {
235         if (value) {
236             this.operationToUpdate.implementation.artifactName = value;
237         }
238         this.validateRequiredField();
239     }
240
241     onArtifactVersionChange(value: any) {
242         if (value) {
243             this.operationToUpdate.implementation.artifactVersion = value;
244         }
245     }
246
247     onAddInput(inputOperationParameter: InputOperationParameter) {
248         this.addInput(inputOperationParameter);
249         this.validityChanged();
250     }
251
252     propertyValueValidation = (propertyValue): void => {
253         this.onPropertyValueChange(propertyValue);
254         this.propertyValueValid = propertyValue.isValid;
255         this.readonly = !this.propertyValueValid;
256         this.validateRequiredField();
257     }
258
259     onRemoveInput = (inputParam: InputOperationParameter): void => {
260         let index = this.inputs.indexOf(inputParam);
261         this.inputs.splice(index, 1);
262         this.validityChanged();
263     }
264
265     private removeImplementationQuote(): void {
266         if (this.operationToUpdate.implementation) {
267             if (!this.operationToUpdate.implementation
268                 || !this.operationToUpdate.implementation.artifactName) {
269                 return;
270             }
271
272             let implementation = this.operationToUpdate.implementation.artifactName.trim();
273
274             if (implementation.startsWith("'") && implementation.endsWith("'")) {
275                 this.operationToUpdate.implementation.artifactName = implementation.slice(1, -1);
276             }
277         }
278     }
279
280     validityChanged = () => {
281         let validState = this.checkFormValidForSubmit();
282         this.input.validityChangedCallback(validState);
283         if (validState) {
284             this.readonly = false;
285         }
286     }
287
288     private getArtifactTypesSelected() {
289         if (this.operationToUpdate.implementation && this.operationToUpdate.implementation.artifactType) {
290             this.artifactName = this.operationToUpdate.implementation.artifactName;
291             this.toscaArtifactTypeSelected = this.operationToUpdate.implementation.artifactType;
292             this.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
293             this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
294             this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
295             this.enableAddArtifactImplementation = true;
296         }
297         this.validateRequiredField();
298     }
299
300     validateRequiredField = () => {
301         this.readonly = true;
302         const isRequiredFieldSelected = this.isRequiredFieldsSelected();
303         this.input.validityChangedCallback(isRequiredFieldSelected);
304         if (isRequiredFieldSelected && this.propertyValueValid) {
305             this.readonly = false;
306         }
307     }
308
309     private isRequiredFieldsSelected() {
310         return this.toscaArtifactTypeSelected && this.artifactName;
311     }
312
313     private checkFormValidForSubmit = (): boolean => {
314         return this.operationToUpdate.name && this.artifactName && this.isParamsValid();
315     }
316
317     private isParamsValid = (): boolean => {
318         const isInputValid = (input) => input.name && input.inputId && input.type;
319         const isValid = this.inputs.every(isInputValid);
320         if (!isValid) {
321             this.readonly = true;
322         }
323         return isValid;
324     }
325
326     toDropDownOption(val: string) {
327         return { value : val, label: val };
328     }
329
330     /**
331      * Handles the input value change event.
332      * @param changedInput the changed input
333      */
334     onInputValueChange(changedInput: InputOperationParameter) {
335         if (changedInput.value instanceof Object) {
336             changedInput.value = JSON.stringify(changedInput.value);
337         }
338         const inputOperationParameter = this.inputs.find(value => value.name == changedInput.name);
339         inputOperationParameter.value = changedInput.value;
340     }
341
342     onArtifactPropertyValueChange(changedProperty: InputOperationParameter) {
343         if (changedProperty.value instanceof Object) {
344             changedProperty.value = JSON.stringify(changedProperty.value);
345         }
346         const property = this.toscaArtifactTypeProperties.find(artifactProperty => artifactProperty.name == changedProperty.name);
347         property.value = changedProperty.value;
348     }
349
350     /**
351      * Handles the add input event.
352      * @param input the input to add
353      * @private
354      */
355     private addInput(input: InputOperationParameter) {
356         this.operationToUpdate.inputs.listToscaDataDefinition.push(input);
357         this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
358     }
359
360     /**
361      * Return a list with current input names.
362      */
363     collectInputNames() {
364         return this.inputs.map((input) => input.name);
365     }
366
367     /**
368      * Handles the delete input event.
369      * @param inputName the name of the input to be deleted
370      */
371     onInputDelete(inputName: string) {
372         const currentInputs = this.operationToUpdate.inputs.listToscaDataDefinition;
373         const input1 = currentInputs.find(value => value.name === inputName);
374         const indexOfInput = currentInputs.indexOf(input1);
375         if (indexOfInput === -1) {
376             console.error(`Could delete input '${inputName}'. Input not found.`);
377             return;
378         }
379         currentInputs.splice(currentInputs.indexOf(input1), 1);
380         this.inputs = Array.from(currentInputs);
381     }
382
383     private convertArtifactsPropertiesToInput(): Array<InputOperationParameter> {
384         if (!this.toscaArtifactTypeProperties) {
385             return [];
386         }
387         const inputList: Array<InputOperationParameter> = [];
388         this.toscaArtifactTypeProperties.forEach(property => {
389             const input = new InputOperationParameter();
390             input.name = property.name;
391             input.type = property.type;
392             input.schema = property.schema;
393             input.toscaDefaultValue = property.defaultValue;
394             input.value = property.value;
395             inputList.push(input);
396         });
397         return inputList;
398     }
399
400     onSelectInterface(dropDownOption: DropDownOption) {
401         if (dropDownOption) {
402             this.setInterfaceType(dropDownOption);
403         } else {
404             this.setInterfaceType(undefined);
405         }
406         this.setInterfaceOperation(undefined);
407         this.interfaceOperationDropDown.selectOption({} as IDropDownOption);
408         this.loadInterfaceTypeOperations();
409     }
410
411     onSelectOperation(dropDownOption: DropDownOption) {
412         if (this.selectedInterfaceType && dropDownOption) {
413             this.setInterfaceOperation(dropDownOption);
414         }
415     }
416
417     private setInterfaceType(dropDownOption: DropDownOption) {
418         this.selectedInterfaceType = dropDownOption ? dropDownOption : undefined;
419         this.interfaceType = dropDownOption ? dropDownOption.value : undefined;
420         this.operationToUpdate.interfaceType = dropDownOption ? dropDownOption.value : undefined;
421         this.operationToUpdate.interfaceId = dropDownOption ? dropDownOption.value : undefined;
422     }
423
424     private setInterfaceOperation(dropDownOption: DropDownOption) {
425         this.operationToUpdate.name = dropDownOption ? dropDownOption.value : undefined;
426         this.operationToUpdate.operationType = dropDownOption ? dropDownOption.value : undefined;
427         this.selectedInterfaceOperation = dropDownOption ? dropDownOption : undefined;
428     }
429 }
430
431 class DropDownOption implements IDropDownOption {
432     value: string;
433     label: string;
434
435     constructor(value: string, label?: string) {
436         this.value = value;
437         this.label = label || value;
438     }
439 }