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