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
index 1618af4..5c5ec1b 100644 (file)
 *  SPDX-License-Identifier: Apache-2.0
 *  ============LICENSE_END=========================================================
 */
-
-import {Component} from '@angular/core';
+import {Component, EventEmitter, Output, ViewChild} from '@angular/core';
+import { FormControl } from '@angular/forms';
 import {UIInterfaceModel} from "../interface-operations.component";
-import {
-    InputOperationParameter,
-    InterfaceOperationModel,
-    IOperationParamsList
-} from "../../../../../models/interfaceOperation";
+import {InputOperationParameter, InterfaceOperationModel, IOperationParamsList} from "../../../../../models/interfaceOperation";
 import {TranslateService} from "../../../../shared/translator/translate.service";
+import {DropdownValue} from "../../../../components/ui/form-components/dropdown/ui-element-dropdown.component";
+import {ArtifactModel} from "../../../../../models/artifacts";
+import {PropertyBEModel} from "../../../../../models/properties-inputs/property-be-model";
+import {PropertyParamRowComponent} from "./property-param-row/property-param-row.component";
+import {PropertyFEModel} from "../../../../../models/properties-inputs/property-fe-model";
+import {IDropDownOption} from 'onap-ui-angular';
+import {ComponentServiceNg2} from "../../../../services/component-services/component.service";
+import {DropDownComponent} from "onap-ui-angular/dist/form-elements/dropdown/dropdown.component";
+import {DataTypeService} from "../../../../services/data-type.service";
+import {Observable} from "rxjs/Observable";
+import {DataTypeModel} from "../../../../../models/data-types";
+import {InstanceFeDetails} from "../../../../../models/instance-fe-details";
+import {TopologyTemplateService} from "app/ng2/services/component-services/topology-template.service";
+import {CustomToscaFunction} from "../../../../../models/default-custom-functions";
 
 @Component({
     selector: 'operation-handler',
@@ -34,97 +44,411 @@ import {TranslateService} from "../../../../shared/translator/translate.service"
     styleUrls: ['./interface-operation-handler.component.less'],
     providers: [TranslateService]
 })
-
 export class InterfaceOperationHandlerComponent {
 
+    @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>();
+    @ViewChild('interfaceOperationDropDown') interfaceOperationDropDown: DropDownComponent;
+
     input: {
+        componentInstanceMap: Map<string, InstanceFeDetails>;
+        toscaArtifactTypes: Array<DropdownValue>;
         selectedInterface: UIInterfaceModel;
         selectedInterfaceOperation: InterfaceOperationModel;
         validityChangedCallback: Function;
+        isViewOnly: boolean;
+        isEdit: boolean;
+        validImplementationProps:boolean;
+        modelName: string;
     };
 
+    dataTypeMap$: Observable<Map<string, DataTypeModel>>;
+    dataTypeMap: Map<string, DataTypeModel>;
     interfaceType: string;
+    artifactVersion: string;
+    artifactName: string;
     interfaceOperationName: string;
     operationToUpdate: InterfaceOperationModel;
     inputs: Array<InputOperationParameter> = [];
+    properties: Array<PropertyParamRowComponent> = [];
     isLoading: boolean = false;
-    readonly: boolean;
+    isViewOnly: boolean;
+    isEdit: boolean;
+    validImplementationProps:boolean;
+    interfaceTypes: Array<DropdownValue> = [];
+    interfaceTypeOptions: Array<DropDownOption> = [];
+    selectedInterfaceType: DropDownOption = undefined;
+    interfaceOperationMap: Map<string, Array<string>> = new Map<string, Array<string>>();
+    interfaceOperationOptions: Array<DropDownOption> = [];
+    selectedInterfaceOperation: DropDownOption = undefined;
+    modelName: string;
+    toscaArtifactTypeSelected: string;
+    toscaArtifactTypeProperties: Array<PropertyBEModel> = [];
+    artifactTypeProperties: Array<InputOperationParameter> = [];
+    toscaArtifactTypes: Array<DropdownValue> = [];
+    componentInstanceMap: Map<string, InstanceFeDetails>;
+    customToscaFunctions: Array<CustomToscaFunction>;
+    enableAddArtifactImplementation: boolean;
+    propertyValueValid: boolean = true;
+    inputTypeOptions: any[];
+    timeoutValue = new FormControl('');
+    timeoutType = new FormControl('');
+
+    constructor(private dataTypeService: DataTypeService,
+                private componentServiceNg2: ComponentServiceNg2,
+                private topologyTemplateService: TopologyTemplateService) {
+    }
 
     ngOnInit() {
-        this.interfaceType = this.input.selectedInterface.displayType();
+        this.isViewOnly = this.input.isViewOnly;
+        this.isEdit = this.input.isEdit;
+        this.validImplementationProps = this.input.validImplementationProps;
+        this.componentInstanceMap =  this.input.componentInstanceMap ? this.input.componentInstanceMap : null;
+        this.interfaceType = this.input.selectedInterface.type;
         this.operationToUpdate = new InterfaceOperationModel(this.input.selectedInterfaceOperation);
         this.operationToUpdate.interfaceId = this.input.selectedInterface.uniqueId;
         this.operationToUpdate.interfaceType = this.input.selectedInterface.type;
+        this.modelName = this.input.modelName;
+        this.timeoutType.setValue('sec');
+        if (this.operationToUpdate.implementation && this.operationToUpdate.implementation.timeout != null) {
+            this.timeoutValue.setValue(this.operationToUpdate.implementation.timeout);
+            let timeout = this.timeoutValue.value / 3600;
+            if (Number.isInteger(timeout)) {
+                if (timeout > 23 && Number.isInteger(timeout / 24)) {
+                    this.timeoutValue.setValue(timeout / 24);
+                    this.timeoutType.setValue("day");
+                } else {
+                    this.timeoutValue.setValue(timeout);
+                    this.timeoutType.setValue("hour");
+                }
+            } else if (Number.isInteger(timeout / 24)) {
+                this.timeoutValue.setValue(timeout / 24);
+                this.timeoutType.setValue("day");
+            }
+        }
+        this.initCustomToscaFunctions();
+        this.initInputs();
+        this.removeImplementationQuote();
+        this.loadInterfaceOperationImplementation();
+
+        this.dataTypeMap$ = new Observable<Map<string, DataTypeModel>>(subscriber => {
+            this.dataTypeService.findAllDataTypesByModel(this.modelName)
+            .then((dataTypesMap: Map<string, DataTypeModel>) => {
+                subscriber.next(dataTypesMap);
+            });
+        });
+        this.dataTypeMap$.subscribe(value => {
+            this.dataTypeMap = value;
+        });
+    }
+
+    private initInputs() {
         if (!this.operationToUpdate.inputs) {
             this.operationToUpdate.inputs = new class implements IOperationParamsList {
                 listToscaDataDefinition: Array<InputOperationParameter> = [];
             }
         }
-        this.inputs = this.operationToUpdate.inputs.listToscaDataDefinition;
+
+        this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
         this.removeImplementationQuote();
-        this.validityChanged();
+        this.loadInterfaceOperationImplementation();
+        this.loadInterfaceType();
+    }
+
+    private initCustomToscaFunctions() {
+        this.customToscaFunctions = [];
+        this.topologyTemplateService.getDefaultCustomFunction().toPromise().then((data) => {
+            if (data) {
+                for (let customFunction of data) {
+                    this.customToscaFunctions.push(new CustomToscaFunction(customFunction));
+                }
+            }
+        });
+    }
+
+    private loadInterfaceType() {
+        this.componentServiceNg2.getInterfaceTypesByModel(this.modelName)
+        .subscribe(response => {
+            if (response) {
+                this.interfaceOperationMap = new Map<string, Array<string>>();
+                for (const interfaceType of Object.keys(response).sort()) {
+                    const operationList = response[interfaceType];
+                    operationList.sort();
+                    this.interfaceOperationMap.set(interfaceType, operationList);
+                    const operationDropDownOption: DropDownOption = new DropDownOption(interfaceType);
+                    this.interfaceTypeOptions.push(operationDropDownOption);
+                    if (this.interfaceType == interfaceType) {
+                        this.selectedInterfaceType = operationDropDownOption;
+                    }
+                }
+                this.loadInterfaceTypeOperations();
+            }
+        });
+    }
+
+    loadInterfaceTypeOperations() {
+        this.interfaceOperationOptions = new Array<DropDownOption>();
+        const interfaceOperationList = this.interfaceOperationMap.get(this.interfaceType);
+
+        if (interfaceOperationList) {
+            interfaceOperationList.forEach(operationName => {
+                const operationOption = new DropDownOption(operationName, operationName);
+                this.interfaceOperationOptions.push(operationOption);
+                if (this.operationToUpdate.name == operationName) {
+                    this.selectedInterfaceOperation = operationOption
+                }
+            });
+        }
+
+        this.interfaceOperationDropDown.allOptions = this.interfaceOperationOptions;
+    }
+
+    private loadInterfaceOperationImplementation() {
+        this.toscaArtifactTypes = this.input.toscaArtifactTypes;
+        if (this.operationToUpdate.implementation) {
+            this.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
+            this.artifactName = this.operationToUpdate.implementation.artifactName;
+            this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
+        }
+        this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
+        this.getArtifactTypesSelected();
+    }
+
+    onDescriptionChange = (value: any): void => {
+        this.operationToUpdate.description = value;
+    }
+
+    onURIChange(value: string | undefined) {
+        if(!this.operationToUpdate.implementation){
+            let artifact = new ArtifactModel();
+            this.operationToUpdate.implementation = artifact;
+        }
+        this.operationToUpdate.implementation.artifactName = value ? value : '';
+    }
+
+    onPropertyValueChange = (propertyValue) => {
+        this.emitter.emit(propertyValue);
+    }
+
+    onMarkToAddArtifactToImplementation(event: boolean) {
+        if (!event) {
+            this.toscaArtifactTypeSelected = undefined;
+            this.artifactVersion = undefined;
+            if (this.operationToUpdate.implementation.artifactType) {
+                this.operationToUpdate.implementation.artifactVersion = '';
+                this.operationToUpdate.implementation.artifactType = '';
+            }
+            this.toscaArtifactTypeProperties = undefined;
+            this.artifactTypeProperties = undefined;
+        } else {
+            this.getArtifactTypesSelected();
+        }
+        this.enableAddArtifactImplementation = event;
+    }
+
+    onSelectToscaArtifactType(type: IDropDownOption) {
+        if (type) {
+            let toscaArtifactType = type.value;
+            let artifact = new ArtifactModel();
+            artifact.artifactName = this.operationToUpdate.implementation.artifactName;
+            artifact.artifactVersion = this.operationToUpdate.implementation.artifactVersion;
+            artifact.artifactType = toscaArtifactType.type;
+            artifact.properties = toscaArtifactType.properties;
+            this.toscaArtifactTypeProperties = artifact.properties;
+            this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
+            this.toscaArtifactTypeSelected = artifact.artifactType;
+            this.operationToUpdate.implementation = artifact;
+            this.getArtifactTypesSelected();
+        }
+    }
+
+    onArtifactVersionChange(value: string | undefined) {
+            this.operationToUpdate.implementation.artifactVersion = value ? value : '';
+    }
+
+    onAddInput(inputOperationParameter: InputOperationParameter) {
+        this.addInput(inputOperationParameter);
     }
 
-    onAddInput(inputOperationParameter?: InputOperationParameter): void {
-        let newInput = new InputOperationParameter(inputOperationParameter)
-        newInput.type = "string";
-        newInput.inputId = this.generateUniqueId();
-        this.inputs.push(newInput);
-        this.validityChanged();
+    propertyValueValidation = (propertyValue): void => {
+        this.onPropertyValueChange(propertyValue);
+        this.propertyValueValid = propertyValue.isValid;
     }
 
     onRemoveInput = (inputParam: InputOperationParameter): void => {
         let index = this.inputs.indexOf(inputParam);
         this.inputs.splice(index, 1);
-        this.validityChanged();
     }
 
-    private generateUniqueId = (): string => {
-        let result = '';
-        const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
-        const charactersLength = characters.length;
-        for (let i = 0; i < 36; i++ ) {
-            result += characters.charAt(Math.floor(Math.random() * charactersLength));
+    timeoutConversion = (): void => {
+        let timeout = this.timeoutValue.value;
+        if (timeout != null) {
+            if (this.timeoutType.value == null || this.timeoutType.value == 'sec') {
+                this.operationToUpdate.implementation.timeout = timeout;
+                return;
+            }
+            if (this.timeoutType.value == 'hour') {
+                this.operationToUpdate.implementation.timeout = timeout * 3600;
+            } else if (this.timeoutType.value == 'day') {
+                this.operationToUpdate.implementation.timeout = (timeout * 24) * 3600;
+            }
         }
-        return result;
     }
 
-    validityChanged = () => {
-        let validState = this.checkFormValidForSubmit();
-        this.input.validityChangedCallback(validState);
-        if (validState) {
-            this.readonly = false;
+    private removeImplementationQuote(): void {
+        if (this.operationToUpdate.implementation) {
+            if (!this.operationToUpdate.implementation
+                || !this.operationToUpdate.implementation.artifactName) {
+                return;
+            }
+
+            let implementation = this.operationToUpdate.implementation.artifactName.trim();
+
+            if (implementation.startsWith("'") && implementation.endsWith("'")) {
+                this.operationToUpdate.implementation.artifactName = implementation.slice(1, -1);
+            }
         }
     }
 
-    onDescriptionChange= (value: any): void => {
-        this.operationToUpdate.description = value;
+    private getArtifactTypesSelected() {
+        if (this.operationToUpdate.implementation && this.operationToUpdate.implementation.artifactType) {
+            this.artifactName =
+                this.artifactName ? this.artifactName : this.operationToUpdate.implementation.artifactName;
+            this.toscaArtifactTypeSelected = this.operationToUpdate.implementation.artifactType;
+            this.artifactVersion =
+                this.artifactVersion ? this.artifactVersion : this.operationToUpdate.implementation.artifactVersion;
+            this.toscaArtifactTypeProperties = this.operationToUpdate.implementation.properties;
+            this.artifactTypeProperties = this.convertArtifactsPropertiesToInput();
+            this.enableAddArtifactImplementation = true;
+        }
     }
 
-    private checkFormValidForSubmit = (): boolean => {
-        return this.operationToUpdate.name && this.isParamsValid();
+    toDropDownOption(val: string) {
+        return { value : val, label: val };
     }
 
-    private isParamsValid = (): boolean => {
-        const isInputValid = (input) => input.name && input.inputId;
-        const isValid = this.inputs.every(isInputValid);
-        if (!isValid) {
-            this.readonly = true;
+    /**
+     * Handles the input value change event.
+     * @param changedInput the changed input
+     */
+    onInputValueChange(changedInput: InputOperationParameter) {
+        if (changedInput.value instanceof Object) {
+            changedInput.value = JSON.stringify(changedInput.value);
+        }
+        const inputOperationParameter = this.inputs.find(value => value.name == changedInput.name);
+        inputOperationParameter.toscaFunction = null;
+        inputOperationParameter.value = changedInput.value;
+        inputOperationParameter.subPropertyToscaFunctions = changedInput.subPropertyToscaFunctions;
+        if (changedInput.isToscaFunction()) {
+            inputOperationParameter.toscaFunction = changedInput.toscaFunction;
+            inputOperationParameter.value = changedInput.toscaFunction.buildValueString();
         }
-        return isValid;
     }
 
-    private removeImplementationQuote(): void {
-        if (!this.operationToUpdate.implementation
-            || !this.operationToUpdate.implementation.artifactName) {
+    onArtifactPropertyValueChange(changedProperty: InputOperationParameter) {
+        const property = this.toscaArtifactTypeProperties.find(artifactProperty => artifactProperty.name == changedProperty.name);
+        if (changedProperty.value instanceof Object) {
+            changedProperty.value = JSON.stringify(changedProperty.value);
+        }
+        property.toscaFunction = null;
+        property.value = changedProperty.value;
+        if (changedProperty.isToscaFunction()) {
+            property.toscaFunction = changedProperty.toscaFunction;
+            property.value = changedProperty.toscaFunction.buildValueString();
+        }
+    }
+
+    implementationPropsValidityChange(validImplementationProps: boolean) {
+        this.validImplementationProps = validImplementationProps;
+    }
+
+    /**
+     * Handles the add input event.
+     * @param input the input to add
+     * @private
+     */
+    private addInput(input: InputOperationParameter) {
+        this.operationToUpdate.inputs.listToscaDataDefinition.push(input);
+        this.inputs = Array.from(this.operationToUpdate.inputs.listToscaDataDefinition);
+    }
+
+    /**
+     * Return a list with current input names.
+     */
+    collectInputNames() {
+        return this.inputs.map((input) => input.name);
+    }
+
+    /**
+     * Handles the delete input event.
+     * @param inputName the name of the input to be deleted
+     */
+    onInputDelete(inputName: string) {
+        const currentInputs = this.operationToUpdate.inputs.listToscaDataDefinition;
+        const input1 = currentInputs.find(value => value.name === inputName);
+        const indexOfInput = currentInputs.indexOf(input1);
+        if (indexOfInput === -1) {
+            console.error(`Could not delete input '${inputName}'. Input not found.`);
             return;
         }
+        currentInputs.splice(currentInputs.indexOf(input1), 1);
+        this.inputs = Array.from(currentInputs);
+    }
 
-        let implementation = this.operationToUpdate.implementation.artifactName.trim();
+    private convertArtifactsPropertiesToInput(): Array<InputOperationParameter> {
+        if (!this.toscaArtifactTypeProperties) {
+            return [];
+        }
+        const inputList: Array<InputOperationParameter> = [];
+        this.toscaArtifactTypeProperties.forEach(property => {
+            const input = new InputOperationParameter();
+            input.name = property.name;
+            input.type = property.type;
+            input.schema = property.schema;
+            input.toscaDefaultValue = property.defaultValue;
+            input.value = property.value;
+            input.toscaFunction = property.toscaFunction;
+            inputList.push(input);
+        });
+        return inputList;
+    }
 
-        if (implementation.startsWith("'") && implementation.endsWith("'")) {
-            this.operationToUpdate.implementation.artifactName = implementation.slice(1, -1);
+    onSelectInterface(dropDownOption: DropDownOption) {
+        if (dropDownOption) {
+            this.setInterfaceType(dropDownOption);
+        } else {
+            this.setInterfaceType(undefined);
         }
+        this.setInterfaceOperation(undefined);
+        this.interfaceOperationDropDown.selectOption({} as IDropDownOption);
+        this.loadInterfaceTypeOperations();
     }
 
+    onSelectOperation(dropDownOption: DropDownOption) {
+        if (this.selectedInterfaceType && dropDownOption) {
+            this.setInterfaceOperation(dropDownOption);
+        }
+    }
+
+    private setInterfaceType(dropDownOption: DropDownOption) {
+        this.selectedInterfaceType = dropDownOption ? dropDownOption : undefined;
+        this.interfaceType = dropDownOption ? dropDownOption.value : undefined;
+        this.operationToUpdate.interfaceType = dropDownOption ? dropDownOption.value : undefined;
+        this.operationToUpdate.interfaceId = dropDownOption ? dropDownOption.value : undefined;
+    }
+
+    private setInterfaceOperation(dropDownOption: DropDownOption) {
+        this.operationToUpdate.name = dropDownOption ? dropDownOption.value : undefined;
+        this.operationToUpdate.operationType = dropDownOption ? dropDownOption.value : undefined;
+        this.selectedInterfaceOperation = dropDownOption ? dropDownOption : undefined;
+    }
 }
+
+class DropDownOption implements IDropDownOption {
+    value: string;
+    label: string;
+
+    constructor(value: string, label?: string) {
+        this.value = value;
+        this.label = label || value;
+    }
+}
\ No newline at end of file