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