Provide timeout field in interface operation implementation
[sdc.git] / catalog-ui / src / app / ng2 / pages / interface-definition / interface-definition.page.component.ts
index 2a77b5e..90d6a6c 100644 (file)
 *  SPDX-License-Identifier: Apache-2.0
 *  ============LICENSE_END=========================================================
 */
-import {Component, Input, Inject, ComponentRef} from '@angular/core';
-import {Component as IComponent } from 'app/models/components/component';
+import {Component, ComponentRef, Inject, Input} from '@angular/core';
+import {Component as IComponent} from 'app/models/components/component';
+import {WorkflowServiceNg2} from 'app/ng2/services/workflow.service';
 
-import { SdcConfigToken, ISdcConfig } from "app/ng2/config/sdc-config.config";
-import {TranslateService } from "app/ng2/shared/translator/translate.service";
+import {ISdcConfig, SdcConfigToken} from "app/ng2/config/sdc-config.config";
+import {TranslateService} from "app/ng2/shared/translator/translate.service";
+import {IModalButtonComponent, SdcUiServices} from 'onap-ui-angular';
+import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
 
-import { ModalComponent } from 'app/ng2/components/ui/modal/modal.component';
-import {ModalService } from 'app/ng2/services/modal.service';
+import {ModalService} from 'app/ng2/services/modal.service';
 import {
-    OperationModel,
-    InterfaceModel,
+    ArtifactModel,
+    ButtonModel,
     CapabilitiesGroup,
-    ButtonModel, ModalModel
+    InputBEModel,
+    InterfaceModel,
+    ModalModel,
+    OperationModel,
+    WORKFLOW_ASSOCIATION_OPTIONS
 } from 'app/models';
 
-import {ComponentServiceNg2 } from 'app/ng2/services/component-services/component.service';
-
-import { SdcUiServices } from 'onap-ui-angular';
+import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service';
 import {TopologyTemplateService} from "../../services/component-services/topology-template.service";
-import {InputOperationParameter, InterfaceOperationModel} from "../../../models/interfaceOperation";
-import {PropertyParamRowComponent} from "../composition/interface-operatons/operation-creator/property-param-row/property-param-row.component";
+import {InterfaceOperationModel} from "../../../models/interfaceOperation";
 import {InterfaceOperationHandlerComponent} from "../composition/interface-operatons/operation-creator/interface-operation-handler.component";
 import {DropdownValue} from "../../components/ui/form-components/dropdown/ui-element-dropdown.component";
+import {ToscaArtifactModel} from "../../../models/toscaArtifact";
+import {ToscaArtifactService} from "../../services/tosca-artifact.service";
+import {InterfaceOperationComponent} from "../interface-operation/interface-operation.page.component";
+import {Observable} from "rxjs/Observable";
+import {PluginsService} from 'app/ng2/services/plugins.service';
 
 export class UIOperationModel extends OperationModel {
     isCollapsed: boolean = true;
@@ -49,7 +57,6 @@ export class UIOperationModel extends OperationModel {
 
     constructor(operation: OperationModel) {
         super(operation);
-
         if (!operation.description) {
             this.description = '';
         }
@@ -74,7 +81,6 @@ export class UIOperationModel extends OperationModel {
     }
 }
 
-// tslint:disable-next-line:max-classes-per-file
 class ModalTranslation {
     CREATE_TITLE: string;
     EDIT_TITLE: string;
@@ -99,16 +105,14 @@ class ModalTranslation {
     }
 }
 
-// tslint:disable-next-line:max-classes-per-file
 export class UIInterfaceModel extends InterfaceModel {
     isCollapsed: boolean = false;
 
-    constructor(interfaceData?: any) {
-        super(interfaceData);
-        this.operations = _.map(
-            this.operations,
-            (operation) => new UIOperationModel(operation)
-        );
+    constructor(interf?: any) {
+        super(interf);
+        if (this.operations) {
+            this.operations = this.operations.map((operation) => new UIOperationModel(operation));
+        }
     }
 
     toggleCollapse() {
@@ -116,24 +120,23 @@ export class UIInterfaceModel extends InterfaceModel {
     }
 }
 
-// tslint:disable-next-line:max-classes-per-file
 @Component({
     selector: 'interface-definition',
     templateUrl: './interface-definition.page.component.html',
     styleUrls: ['interface-definition.page.component.less'],
-    providers: [ModalService, TranslateService]
+    providers: [ModalService, TranslateService, InterfaceOperationComponent]
 })
-
 export class InterfaceDefinitionComponent {
 
     modalInstance: ComponentRef<ModalComponent>;
     interfaces: UIInterfaceModel[];
-    inputs: Array<InputOperationParameter> = [];
+    inputs: InputBEModel[];
 
-    properties: Array<PropertyParamRowComponent> = [];
     deploymentArtifactsFilePath: Array<DropdownValue> = [];
 
     toscaArtifactTypes: Array<DropdownValue> = [];
+    interfaceTypesTest: Array<DropdownValue> = [];
+    interfaceTypesMap: Map<string, string[]>;
 
     isLoading: boolean;
     interfaceTypes: { [interfaceType: string]: string[] };
@@ -141,6 +144,11 @@ export class InterfaceDefinitionComponent {
     workflows: any[];
     capabilities: CapabilitiesGroup;
 
+    openOperation: OperationModel;
+    enableWorkflowAssociation: boolean;
+    workflowIsOnline: boolean;
+    validImplementationProps:boolean = true;
+
     @Input() component: IComponent;
     @Input() readonly: boolean;
     @Input() enableMenuItems: Function;
@@ -149,18 +157,59 @@ export class InterfaceDefinitionComponent {
     constructor(
         @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
         @Inject("$state") private $state: ng.ui.IStateService,
+        @Inject("Notification") private notification: any,
         private translateService: TranslateService,
         private componentServiceNg2: ComponentServiceNg2,
         private modalServiceNg2: ModalService,
         private modalServiceSdcUI: SdcUiServices.ModalService,
-        private topologyTemplateService: TopologyTemplateService
+        private topologyTemplateService: TopologyTemplateService,
+        private toscaArtifactService: ToscaArtifactService,
+        private ComponentServiceNg2: ComponentServiceNg2,
+        private WorkflowServiceNg2: WorkflowServiceNg2,
+        private ModalServiceSdcUI: SdcUiServices.ModalService,
+        private PluginsService: PluginsService
     ) {
         this.modalTranslation = new ModalTranslation(translateService);
+        this.interfaceTypesMap = new Map<string, string[]>();
     }
 
     ngOnInit(): void {
-        if(this.component) {
-            this.initInterfaceDefinition();
+        this.isLoading = true;
+        this.interfaces = [];
+        this.workflowIsOnline = !_.isUndefined(this.PluginsService.getPluginByStateUrl('workflowDesigner'));
+        Observable.forkJoin(
+            this.ComponentServiceNg2.getInterfaceOperations(this.component),
+            this.ComponentServiceNg2.getComponentInputs(this.component),
+            this.ComponentServiceNg2.getInterfaceTypes(this.component),
+            this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.component.componentType, this.component.uniqueId)
+        ).subscribe((response: any[]) => {
+            const callback = (workflows) => {
+                this.isLoading = false;
+                this.initInterfaces(response[0].interfaces);
+                this.sortInterfaces();
+                this.inputs = response[1].inputs;
+                this.interfaceTypes = response[2];
+                this.workflows = (workflows.items) ? workflows.items : workflows;
+                this.capabilities = response[3].capabilities;
+            };
+            if (this.enableWorkflowAssociation && this.workflowIsOnline) {
+                this.WorkflowServiceNg2.getWorkflows().subscribe(
+                    callback,
+                    (err) => {
+                        this.workflowIsOnline = false;
+                        callback([]);
+                    }
+                );
+            } else {
+                callback([]);
+            }
+        });
+        this.loadToscaArtifacts();
+    }
+
+    initInterfaces(interfaces: InterfaceModel[]): void {
+        if (interfaces) {
+            this.interfaces = interfaces.map((interf) => new UIInterfaceModel(interf));
         }
     }
 
@@ -168,16 +217,43 @@ export class InterfaceDefinitionComponent {
         return this.modalServiceNg2.closeCurrentModal();
     }
 
-    private enableOrDisableSaveButton = (): boolean => {
-        return true;
+    private disableSaveButton = (): boolean => {
+        let disable:boolean = true;
+        if(this.readonly) {
+            return disable;
+        }
+    
+        let selectedInterfaceOperation = this.modalInstance.instance.dynamicContent.instance.selectedInterfaceOperation;
+        let isInterfaceOperation:boolean = !(typeof selectedInterfaceOperation == 'undefined' || _.isEmpty(selectedInterfaceOperation));
+        let selectedInterfaceType = this.modalInstance.instance.dynamicContent.instance.selectedInterfaceType;
+        let isInterfaceType:boolean = !(typeof selectedInterfaceType == 'undefined' || _.isEmpty(selectedInterfaceType));
+        let bothSet: boolean = isInterfaceOperation && isInterfaceType;
+    
+        let enableAddArtifactImplementation = this.modalInstance.instance.dynamicContent.instance.enableAddArtifactImplementation;
+        if(enableAddArtifactImplementation) {
+            let validImplementationProps = this.modalInstance.instance.dynamicContent.instance.validImplementationProps;
+            let toscaArtifactTypeSelected = this.modalInstance.instance.dynamicContent.instance.toscaArtifactTypeSelected;
+            let isToscaArtifactType:boolean = !(typeof toscaArtifactTypeSelected == 'undefined' || _.isEmpty(toscaArtifactTypeSelected));
+            disable = !bothSet || !isToscaArtifactType || !validImplementationProps;
+            return disable;
+        }
+        disable = !bothSet;
+        return disable;
     }
 
     onSelectInterfaceOperation(interfaceModel: UIInterfaceModel, operation: InterfaceOperationModel) {
-        const cancelButton: ButtonModel = new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal);
-        const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue', () =>
-        null, this.enableOrDisableSaveButton);
+        const isEdit = operation !== undefined;
+        const modalButtons = [];
+        if (!this.readonly) {
+            const saveButton: ButtonModel = new ButtonModel(this.modalTranslation.SAVE_BUTTON, 'blue',
+                () => isEdit ? this.updateOperation() : this.createOperationCallback(),
+                this.disableSaveButton
+            );
+            modalButtons.push(saveButton);
+        }
+        modalButtons.push(new ButtonModel(this.modalTranslation.CANCEL_BUTTON, 'outline white', this.cancelAndCloseModal));
         const interfaceDataModal: ModalModel =
-            new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', [saveButton, cancelButton], 'custom');
+            new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', modalButtons, 'custom');
         this.modalInstance = this.modalServiceNg2.createCustomModal(interfaceDataModal);
 
         this.modalServiceNg2.addDynamicContentToModal(
@@ -186,39 +262,152 @@ export class InterfaceDefinitionComponent {
             {
                 deploymentArtifactsFilePath: this.deploymentArtifactsFilePath,
                 toscaArtifactTypes: this.toscaArtifactTypes,
-                selectedInterface: interfaceModel,
-                selectedInterfaceOperation: operation,
-                validityChangedCallback: this.enableOrDisableSaveButton,
-                isViewOnly: true
+                selectedInterface: interfaceModel ? interfaceModel : new UIInterfaceModel(),
+                selectedInterfaceOperation: operation ? operation : new InterfaceOperationModel(),
+                validityChangedCallback: this.disableSaveButton,
+                isViewOnly: this.readonly,
+                validImplementationProps: this.validImplementationProps,
+                'isEdit': isEdit,
+                interfaceTypesMap: this.interfaceTypesMap,
+                modelName: this.component.model
             }
         );
         this.modalInstance.instance.open();
     }
 
+    private updateOperation = (): void => {
+        this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
+        const interfaceOperationHandlerComponentInstance: InterfaceOperationHandlerComponent = this.modalInstance.instance.dynamicContent.instance;
+        const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
+        let timeout = null;
+        if (operationToUpdate.implementation && operationToUpdate.implementation.timeout != null) {
+            timeout = operationToUpdate.implementation.timeout;
+        }
+        const isArtifactChecked = interfaceOperationHandlerComponentInstance.enableAddArtifactImplementation;
+        if (!isArtifactChecked) {
+            const artifactName = interfaceOperationHandlerComponentInstance.artifactName ?
+                interfaceOperationHandlerComponentInstance.artifactName : '';
+            operationToUpdate.implementation = new ArtifactModel({'artifactName': artifactName, 'artifactVersion': ''} as ArtifactModel);
+        }
+        if (timeout != null) {
+            operationToUpdate.implementation.timeout = timeout;
+        }
+        this.componentServiceNg2.updateComponentInterfaceOperation(this.component.uniqueId, operationToUpdate)
+        .subscribe((newOperation: InterfaceOperationModel) => {
+            let oldOpIndex;
+            let oldInterf;
+            this.interfaces.forEach(interf => {
+                interf.operations.forEach(op => {
+                    if (op.uniqueId === newOperation.uniqueId) {
+                        oldInterf = interf;
+                        oldOpIndex = interf.operations.findIndex((el) => el.uniqueId === op.uniqueId);
+                    }
+                });
+            });
+            oldInterf.operations.splice(oldOpIndex, 1);
+            oldInterf.operations.push(new InterfaceOperationModel(newOperation));
+        }, error => {
+            this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
+        }, () => {
+            this.sortInterfaces();
+            this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
+            this.modalServiceNg2.closeCurrentModal();
+        });
+    }
+
+    private createOperationCallback(): void {
+        this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = true;
+        const operationToUpdate = this.modalInstance.instance.dynamicContent.instance.operationToUpdate;
+        console.log('createOperationCallback', operationToUpdate);
+        console.log('this.component', this.component);
+        this.componentServiceNg2.createComponentInterfaceOperation(this.component.uniqueId, this.component.getTypeUrl(), operationToUpdate)
+        .subscribe((newOperation: InterfaceOperationModel) => {
+            const foundInterface = this.interfaces.find(value => value.type === newOperation.interfaceType);
+            if (foundInterface) {
+                foundInterface.operations.push(new UIOperationModel(new OperationModel(newOperation)));
+            } else {
+                const uiInterfaceModel = new UIInterfaceModel();
+                uiInterfaceModel.type = newOperation.interfaceType;
+                uiInterfaceModel.uniqueId = newOperation.interfaceType;
+                uiInterfaceModel.operations = [];
+                uiInterfaceModel.operations.push(new UIOperationModel(new OperationModel(newOperation)));
+                this.interfaces.push(uiInterfaceModel);
+            }
+        }, error => {
+            this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
+        }, () => {
+            this.modalServiceNg2.currentModal.instance.dynamicContent.instance.isLoading = false;
+            this.modalServiceNg2.closeCurrentModal();
+        });
+    }
+
+    private handleEnableAddArtifactImplementation = (newOperation: InterfaceOperationModel): InterfaceOperationModel => {
+        if (!this.isEnableAddArtifactImplementation()) {
+            newOperation.implementation.artifactType = null;
+            newOperation.implementation.artifactVersion = null;
+        }
+        return newOperation;
+    }
+
+    private isEnableAddArtifactImplementation = (): boolean => {
+        return this.modalInstance.instance.dynamicContent.enableAddArtifactImplementation;
+    }
+
     private initInterfaceDefinition() {
         this.isLoading = true;
         this.interfaces = [];
         this.topologyTemplateService.getComponentInterfaceOperations(this.component.componentType, this.component.uniqueId)
         .subscribe((response) => {
             if (response.interfaces) {
-                this.interfaces = _.map(response.interfaces, (interfaceModel) => new UIInterfaceModel(interfaceModel));
+                this.interfaces = response.interfaces.map((interfaceModel) => new UIInterfaceModel(interfaceModel));
             }
             this.isLoading = false;
         });
     }
 
+    private loadToscaArtifacts() {
+        this.toscaArtifactService.getToscaArtifacts(this.component.model).subscribe(response => {
+            if (response) {
+                let toscaArtifactsFound = <ToscaArtifactModel[]>_.values(response);
+                toscaArtifactsFound.forEach(value => this.toscaArtifactTypes.push(new DropdownValue(value, value.type)));
+            }
+        }, error => {
+            this.notification.error({
+                message: 'Failed to Load Tosca Artifacts:' + error,
+                title: 'Failure'
+            });
+        });
+    }
+
+    private loadInterfaceTypes() {
+        this.componentServiceNg2.getInterfaceTypes(this.component).subscribe(response => {
+            if (response) {
+                console.info("loadInterfaceTypes ", response);
+                for (const interfaceType in response) {
+                    this.interfaceTypesMap.set(interfaceType, response[interfaceType]);
+                    this.interfaceTypesTest.push(new DropdownValue(interfaceType, interfaceType));
+                }
+            }
+        }, error => {
+            this.notification.error({
+                message: 'Failed to Load Interface Types:' + error,
+                title: 'Failure'
+            });
+        });
+    }
+
     collapseAll(value: boolean = true): void {
-        _.forEach(this.interfaces, (interfaceData) => {
+        this.interfaces.forEach(interfaceData => {
             interfaceData.isCollapsed = value;
         });
     }
 
     isAllCollapsed(): boolean {
-        return _.every(this.interfaces, (interfaceData) => interfaceData.isCollapsed);
+        return this.interfaces.every((interfaceData) => interfaceData.isCollapsed);
     }
 
     isAllExpanded(): boolean {
-        return _.every(this.interfaces, (interfaceData) => !interfaceData.isCollapsed);
+        return this.interfaces.every((interfaceData) => !interfaceData.isCollapsed);
     }
 
     isInterfaceListEmpty(): boolean {
@@ -226,8 +415,94 @@ export class InterfaceDefinitionComponent {
     }
 
     isOperationListEmpty(): boolean {
-        return _.filter(this.interfaces, (interfaceData) =>
-            interfaceData.operations && interfaceData.operations.length > 0).length > 0;
+        return this.interfaces.filter((interfaceData) => interfaceData.operations && interfaceData.operations.length > 0).length > 0;
+    }
+
+    onRemoveOperation(operation: OperationModel): void {
+        if (this.readonly) {
+            return;
+        }
+
+        const deleteButton: IModalButtonComponent = {
+            id: 'deleteButton',
+            text: this.modalTranslation.DELETE_BUTTON,
+            type: 'primary',
+            size: 'small',
+            closeModal: true,
+            callback: () => {
+                this.ComponentServiceNg2
+                .deleteInterfaceOperation(this.component, operation)
+                .subscribe(() => {
+                    const curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
+                    const index = curInterf.operations.findIndex((el) => el.uniqueId === operation.uniqueId);
+                    curInterf.operations.splice(index, 1);
+                    if (!curInterf.operations.length) {
+                        const interfIndex = this.interfaces.findIndex((interf) => interf.type === operation.interfaceType);
+                        this.interfaces.splice(interfIndex, 1);
+                    }
+                });
+            }
+        };
+
+        const cancelButton: IModalButtonComponent = {
+            id: 'cancelButton',
+            text: this.modalTranslation.CANCEL_BUTTON,
+            type: 'secondary',
+            size: 'small',
+            closeModal: true,
+            callback: () => {
+                this.openOperation = null;
+            },
+        };
+
+        this.ModalServiceSdcUI.openWarningModal(
+            this.modalTranslation.DELETE_TITLE,
+            this.modalTranslation.deleteText(operation.name),
+            'deleteOperationModal',
+            [deleteButton, cancelButton],
+        );
+    }
+
+    private createOperation = (operation: OperationModel): void => {
+        this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: OperationModel) => {
+            this.openOperation = null;
+
+            let curInterf = this.interfaces.find((interf) => interf.type === operation.interfaceType);
+
+            if (!curInterf) {
+                curInterf = new UIInterfaceModel({
+                    type: response.interfaceType,
+                    uniqueId: response.uniqueId,
+                    operations: []
+                });
+                this.interfaces.push(curInterf);
+            }
+
+            const newOpModel = new UIOperationModel(response);
+            curInterf.operations.push(newOpModel);
+            this.sortInterfaces();
+
+            if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXTERNAL && operation.artifactData) {
+                this.ComponentServiceNg2.uploadInterfaceOperationArtifact(this.component, newOpModel, operation).subscribe();
+            } else if (response.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
+                this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, response).subscribe();
+            } else if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW) {
+                this.$state.go('workspace.plugins', {path: 'workflowDesigner'});
+            }
+        });
+    }
+
+    private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
+        const saveButton = this.modalInstance.instance.dynamicContent.getButtonById('saveButton');
+        saveButton.disabled = !shouldEnable;
+    }
+
+    private sortInterfaces(): void {
+        this.interfaces = this.interfaces.filter((interf) => interf.operations && interf.operations.length > 0); // remove empty interfaces
+        this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
+        this.interfaces.forEach((interf) => {
+            interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
+        });
     }
 
 }