Interface Operation tab and screens 33/43033/6
authorArielk <Ariel.Kenan@amdocs.com>
Mon, 16 Apr 2018 12:37:39 +0000 (15:37 +0300)
committerAvi Gaffa <avi.gaffa@amdocs.com>
Tue, 1 May 2018 03:38:57 +0000 (03:38 +0000)
Change-Id: If03234c783d6ce16fdd0945987ada83b6285f97f
Issue-ID: SDC-1060
Signed-off-by: Arielk <Ariel.Kenan@amdocs.com>
28 files changed:
catalog-ui/configurations/menu.js
catalog-ui/src/app/app.ts
catalog-ui/src/app/models.ts
catalog-ui/src/app/models/components/component.ts
catalog-ui/src/app/models/operation.ts [new file with mode: 0644]
catalog-ui/src/app/modules/directive-module.ts
catalog-ui/src/app/modules/view-model-module.ts
catalog-ui/src/app/ng2/app.module.ts
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/services/component-services/component.service.ts
catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
catalog-ui/src/app/utils/common-utils.ts
catalog-ui/src/app/utils/constants.ts
catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation-view-model.ts [new file with mode: 0644]
catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation-view.html [new file with mode: 0644]
catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation.less [new file with mode: 0644]
catalog-ui/src/app/view-models/workspace/tabs/plugins/plugins-context-view-model.ts
catalog-ui/src/assets/styles/app.less

index 9bcf89c..99cb2f7 100644 (file)
@@ -334,6 +334,7 @@ const SDC_MENU_CONFIG = {
             {"text":"TOSCA Artifacts", "action":"onMenuItemPressed", "state": "workspace.tosca_artifacts"},
             // {"text":"Properties", "action":"onMenuItemPressed", "state": "workspace.properties"},
             {"text":"Composition", "action":"onMenuItemPressed", "state": "workspace.composition.details"},
+            {"text":"Operation", "action":"onMenuItemPressed", "state": "workspace.interface_operation"},
             {"text":"Activity Log", "action":"onMenuItemPressed", "state": "workspace.activity_log"},
             {"text":"Deployment", "action":"onMenuItemPressed", "state": "workspace.deployment"},
             // {"text":"Inputs", "action":"onMenuItemPressed", "state": "workspace.resource_inputs"},
index 48f15c0..a9ec17c 100644 (file)
@@ -507,10 +507,23 @@ ng1appModule.config([
             }
         );
 
+        $stateProvider.state(
+            States.WORKSPACE_INTERFACE_OPERATION, {
+                url: 'interface_operation',
+                parent: 'workspace',
+                controller: viewModelsModuleName + '.InterfaceOperationViewModel',
+                templateUrl: './view-models/workspace/tabs/interface-operation/interface-operation-view.html',
+                data: {
+                    bodyClass: 'interface_operation'
+                }
+            }
+        );
+
         $stateProvider.state(
             'workspace.plugins', {
                 url: 'plugins/*path',
                 parent: 'workspace',
+                params: {'queryParams': null},
                 templateUrl: './view-models/workspace/tabs/plugins/plugins-context-view.html',
                 controller: viewModelsModuleName + '.PluginsContextViewModel'
             }
index ac26302..3a48335 100644 (file)
@@ -97,6 +97,7 @@ export * from './models/instance-inputs-properties-map';
 export * from './models/left-panel';
 export * from './models/member';
 export * from './models/modules/base-module';
+export * from './models/operation';
 export * from './models/properties';
 export * from './models/requirement';
 export * from './models/server-error-response';
index b7f5722..adcf498 100644 (file)
@@ -23,7 +23,7 @@
 import * as _ from "lodash";
 import {AsdcComment, ArtifactModel, ArtifactGroupModel, IFileDownload, PropertyModel, PropertiesGroup, AttributeModel, AttributesGroup, ComponentInstance,
     InputModel, DisplayModule, Module, IValidate, RelationshipModel, IMainCategory, RequirementsGroup, CapabilitiesGroup, AdditionalInformationModel,
-    Resource, IAppMenu, Service} from "../../models";
+    Resource, IAppMenu, OperationModel, Service} from "../../models";
 
 import {IComponentService} from "../../services/components/component-service";
 import {CommonUtils} from "../../utils/common-utils";
@@ -134,6 +134,7 @@ export abstract class Component implements IComponent {
     public deploymentArtifacts:ArtifactGroupModel;
     public artifacts:ArtifactGroupModel;
     public toscaArtifacts:ArtifactGroupModel;
+    public interfaceOperations:Array<OperationModel>;
     public distributionStatus:string;
     public categories:Array<IMainCategory>;
     public categoryNormalizedName: string;
@@ -178,6 +179,7 @@ export abstract class Component implements IComponent {
             this.additionalInformation = component.additionalInformation;
             this.artifacts = new ArtifactGroupModel(component.artifacts);
             this.toscaArtifacts = new ArtifactGroupModel(component.toscaArtifacts);
+            this.interfaceOperations = component.interfaceOperations;
             this.contactId = component.contactId;
             this.categories = component.categories;
             this.categoryNormalizedName = component.categoryNormalizedName;
diff --git a/catalog-ui/src/app/models/operation.ts b/catalog-ui/src/app/models/operation.ts
new file mode 100644 (file)
index 0000000..55fcd82
--- /dev/null
@@ -0,0 +1,51 @@
+'use strict';
+
+export class OperationParam {
+    paramName: string = '';
+    paramId: string = '';
+
+    constructor(param?: OperationParam) {
+        if (param) {
+            this.paramId = param.paramId;
+            this.paramName = param.paramName;
+        }
+    }
+}
+
+export interface IOperationParamsList {
+    listToscaDataDefinition: Array<OperationParam>;
+}
+
+export class OperationModel {
+    description: string;
+    inputParams: IOperationParamsList;
+    operationType: string;
+    outputParams: IOperationParamsList;
+    uniqueId: string;
+
+    constructor(operation?: any) {
+        if (operation) {
+            this.description = operation.description;
+            this.inputParams = operation.inputParams;
+            this.operationType = operation.operationType;
+            this.outputParams = operation.outputParams;
+            this.uniqueId = operation.uniqueId;
+        }
+    }
+
+    public createInputParamsList(inputParams: Array<OperationParam>): void {
+        this.inputParams = {
+            listToscaDataDefinition: inputParams
+        };
+    }
+
+    public createOutputParamsList(outputParams: Array<OperationParam>): void {
+        this.outputParams = {
+            listToscaDataDefinition: outputParams
+        };
+    }
+}
+
+export interface CreateOperationResponse extends OperationModel {
+    artifactUUID: string;
+}
index 6285415..d409da0 100644 (file)
@@ -183,7 +183,8 @@ import { SearchWithAutoCompleteComponent } from "../ng2/components/ui/search-wit
 import { PalettePopupPanelComponent } from "../ng2/components/ui/palette-popup-panel/palette-popup-panel.component";
 import { ServicePathComponent } from '../ng2/components/logic/service-path/service-path.component';
 import { ServicePathSelectorComponent } from '../ng2/components/logic/service-path-selector/service-path-selector.component';
-import {PluginFrameComponent} from "../ng2/components/ui/plugin/plugin-frame.component";
+import { InterfaceOperationComponent } from '../ng2/pages/interface-operation/interface-operation.page.component';
+import { PluginFrameComponent } from "../ng2/components/ui/plugin/plugin-frame.component";
 
 directiveModule.directive('menuListNg2', downgradeComponent({
     component: MenuListNg2Component,
@@ -242,6 +243,12 @@ directiveModule.directive('ng2ServicePathSelector', downgradeComponent({
     outputs: []
 }) as angular.IDirectiveFactory);
 
+directiveModule.directive('ng2InterfaceOperation', downgradeComponent({
+    component: InterfaceOperationComponent,
+    inputs: ['component', 'readonly', 'state'],
+    outputs: []
+}) as angular.IDirectiveFactory);
+
 directiveModule.directive('pluginFrame', downgradeComponent( {
     component: PluginFrameComponent,
     inputs: ['plugin', 'queryParams'],
index a4f47ff..de7d6d8 100644 (file)
@@ -64,6 +64,7 @@ import {PropertiesViewModel} from "../view-models/workspace/tabs/properties/prop
 import {AttributesViewModel} from "../view-models/workspace/tabs/attributes/attributes-view-model";
 import {ActivityLogViewModel} from "../view-models/workspace/tabs/activity-log/activity-log";
 import {ManagementWorkflowViewModel} from "../view-models/workspace/tabs/management-workflow/management-workflow-view-model";
+import {InterfaceOperationViewModel} from "../view-models/workspace/tabs/interface-operation/interface-operation-view-model";
 import {NetworkCallFlowViewModel} from "../view-models/workspace/tabs/network-call-flow/network-call-flow-view-model";
 import {DeploymentViewModel} from "../view-models/workspace/tabs/deployment/deployment-view-model";
 import {ResourceInputsViewModel} from "../view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model";
@@ -131,6 +132,7 @@ viewModelModule
   .controller(moduleName + '.AttributesViewModel', AttributesViewModel)
   .controller(moduleName + '.ActivityLogViewModel', ActivityLogViewModel)
   .controller(moduleName + '.ManagementWorkflowViewModel', ManagementWorkflowViewModel)
+  .controller(moduleName + '.InterfaceOperationViewModel', InterfaceOperationViewModel)
   .controller(moduleName + '.NetworkCallFlowViewModel', NetworkCallFlowViewModel)
   .controller(moduleName + '.DeploymentViewModel', DeploymentViewModel)
   .controller(moduleName + '.ResourceInputsViewModel', ResourceInputsViewModel)
index 727f0fe..194d2e0 100644 (file)
@@ -43,6 +43,8 @@ import {ComponentInstanceServiceNg2} from "./services/component-instance-service
 import {ModalService} from "./services/modal.service";
 import {UiElementsModule} from "./components/ui/ui-elements.module";
 import {ConnectionWizardModule} from "./pages/connection-wizard/connection-wizard.module";
+import {InterfaceOperationModule} from "./pages/interface-operation/interface-operation.module";
+import {OperationCreatorModule} from "./pages/interface-operation/operation-creator/operation-creator.module";
 import {LayoutModule} from "./components/layout/layout.module";
 import {UserService} from "./services/user.service";
 import {PoliciesService} from "./services/policies.service";
@@ -85,6 +87,8 @@ export function configServiceFactory(config:ConfigService) {
         ConnectionWizardModule,
         PropertiesAssignmentModule,
         PluginFrameModule,
+        InterfaceOperationModule,
+        OperationCreatorModule,
         ServicePathCreatorModule,
         ServicePathsListModule,
         ServicePathModule,
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts
new file mode 100644 (file)
index 0000000..caf6a3f
--- /dev/null
@@ -0,0 +1,19 @@
+import {NgModule} from "@angular/core";
+import {CommonModule} from "@angular/common";
+import {InterfaceOperationComponent} from "./interface-operation.page.component";
+
+@NgModule({
+    declarations: [
+        InterfaceOperationComponent
+    ],
+    imports: [
+        CommonModule
+    ],
+    exports: [],
+    entryComponents: [
+        InterfaceOperationComponent
+    ],
+    providers: []
+})
+
+export class InterfaceOperationModule {}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html
new file mode 100644 (file)
index 0000000..25e8672
--- /dev/null
@@ -0,0 +1,38 @@
+<div class="interface-operation">
+    <div
+        class="add-btn"
+        [ngClass]="{'disabled': readonly}"
+        data-tests-id="addInterfaceOperation"
+        (click)="onAddOperation()">
+        Add Operation
+    </div>
+
+    <div class="operation-list" [ngClass]="{'disabled': readonly}">
+
+        <div
+            class="operation-row"
+            *ngFor="let operation of operationList"
+            (click)="onEditOperation(operation)">
+
+            <span class="operation-info-container">
+                <span class="operation-title">
+                    <p class="operation-text" data-tests-id="interfaceOperationType">{{operation.operationType}}</p>
+                </span>
+
+                <span class="operation-description">
+                    <p class="operation-text" data-tests-id="interfaceOperationDescription">{{operation.description}}</p>
+                </span>
+            </span>
+
+            <span class="operation-dumbo">
+                <span
+                    class="sprite-new delete-item-icon"
+                    data-tests-id="deleteInterfaceOperation"
+                    (click)="onRemoveOperation($event, operation)">
+                </span>
+            </span>
+
+        </div>
+    </div>
+
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less
new file mode 100644 (file)
index 0000000..4355020
--- /dev/null
@@ -0,0 +1,88 @@
+@import '../../../../assets/styles/variables.less';
+
+.interface-operation {
+    .add-btn {
+        position: relative;
+        top: -31px;
+        text-transform: uppercase;
+        font-size: 14px;
+    }
+
+    a:not(.disabled) {
+        &:hover {
+            cursor: pointer;
+        }
+    }
+
+    .operation-list {
+        border-top: 1px solid @main_color_o;
+        padding-top: 25px;
+
+        .operation-row {
+            width: 100%;
+            border: 1px solid @main_color_o;
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            height: 100px;
+
+            &:hover {
+                border-color: @main_color_c;
+                cursor: pointer;
+            }
+
+            &:not(:first-child) {
+                margin-top: 20px;
+            }
+
+            .operation-info-container {
+                height: 100%;
+                display: flex;
+                align-items: center;
+                margin-right: 60px;
+
+                .operation-title, .operation-description {
+                    display: flex;
+                    align-items: center;
+
+                    .operation-text {
+                        overflow: hidden;
+                        margin-bottom: 0;
+                        max-height: 5rem;
+                    }
+                }
+
+                .operation-title {
+                    flex-shrink: 0;
+                    width: 200px;
+                    height: calc(100% - 13px);
+                    border-right: 1px solid @main_color_o;
+                    margin: 4px 0;
+                    padding: 0 30px;
+
+                    .operation-text {
+                        white-space: nowrap;
+                        text-overflow: ellipsis;
+                    }
+                }
+
+                .operation-description {
+                    padding-left: 30px;
+                    text-align: left;
+                    font-family: @font-opensans-regular;
+
+                    .operation-text {
+                        word-break: break-word;
+                    }
+                }
+            }
+
+            .operation-dumbo {
+                padding-right: 20px;
+                display: flex;
+                flex-direction: column;
+                align-items: left;
+            }
+        }
+    }
+}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.ts b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.ts
new file mode 100644 (file)
index 0000000..c58e1de
--- /dev/null
@@ -0,0 +1,176 @@
+import * as _ from "lodash";
+import {Component, Input, ComponentRef, Inject} from '@angular/core';
+import {Component as IComponent} from 'app/models/components/component';
+import {ModalService} from 'app/ng2/services/modal.service';
+import {ModalModel, ButtonModel, InputModel, OperationModel, CreateOperationResponse} from 'app/models';
+import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
+import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service';
+import {ComponentGenericResponse} from 'app/ng2/services/responses/component-generic-response';
+import {OperationCreatorComponent} from './operation-creator/operation-creator.component';
+
+@Component({
+    selector: 'interface-operation',
+    templateUrl: './interface-operation.page.component.html',
+    styleUrls: ['interface-operation.page.component.less'],
+    providers: [ModalService]
+})
+
+export class InterfaceOperationComponent {
+
+    modalInstance: ComponentRef<ModalComponent>;
+    operationList: Array<OperationModel> = [];
+    openOperation: OperationModel;
+
+    @Input() component: IComponent;
+    @Input() readonly: boolean;
+
+    constructor(
+        @Inject('$state') private $state:ng.ui.IStateService,
+        private ComponentServiceNg2: ComponentServiceNg2,
+        private ModalServiceNg2: ModalService,
+    ) {}
+
+    ngOnInit(): void {
+        this.ComponentServiceNg2.getInterfaceOperations(this.component).subscribe((response: ComponentGenericResponse) => {
+            let {interfaceOperations} = response;
+            this.component.interfaceOperations = interfaceOperations;
+            this.operationList = _.toArray(interfaceOperations);
+        });
+    }
+
+    getDisabled = (): boolean => {
+        return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit();
+    }
+
+    onEditOperation = (operation: OperationModel): void => {
+        this.ComponentServiceNg2
+            .getInterfaceOperation(this.component, operation)
+            .subscribe(op => this.onAddOperation(op));
+    }
+
+    onAddOperation = (operation?: OperationModel): void => {
+        const modalMap = {
+            create: {
+                modalTitle: 'Create a New Operation',
+                saveBtnText: 'Create',
+                submitCallback: this.createOperation,
+            },
+            edit: {
+                modalTitle: 'Edit Operation',
+                saveBtnText: 'Save',
+                submitCallback: this.updateOperation,
+            }
+        };
+
+        const modalData = operation ? modalMap.edit : modalMap.create;
+
+        if (this.openOperation) {
+            if (operation ? operation.uniqueId === this.openOperation.uniqueId : !this.openOperation.uniqueId) {
+                operation = this.openOperation;
+            }
+        }
+
+        this.ComponentServiceNg2.getComponentInputs(this.component).subscribe((response: ComponentGenericResponse) => {
+
+            const cancelButton: ButtonModel = new ButtonModel(
+                'Cancel',
+                'outline white',
+                () => {
+                    this.openOperation = null;
+                    this.ModalServiceNg2.closeCurrentModal();
+                },
+            );
+
+            const saveButton: ButtonModel = new ButtonModel(
+                modalData.saveBtnText,
+                'blue',
+                () => {
+                    this.modalInstance.instance.dynamicContent.instance.createInputParamList();
+                    this.ModalServiceNg2.closeCurrentModal();
+                    const {operation} = this.modalInstance.instance.dynamicContent.instance;
+                    this.openOperation = operation;
+                    modalData.submitCallback(operation);
+                },
+                this.getDisabled,
+            );
+
+            const modalModel: ModalModel = new ModalModel(
+                'l',
+                modalData.modalTitle,
+                '',
+                [saveButton, cancelButton],
+                'standard',
+            );
+
+            this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel);
+            this.ModalServiceNg2.addDynamicContentToModal(
+                this.modalInstance,
+                OperationCreatorComponent,
+                {
+                    operation,
+                    inputProperties: response.inputs,
+                },
+            );
+
+            this.modalInstance.instance.open();
+        });
+    }
+
+    onRemoveOperation = (event: Event, operation: OperationModel): void => {
+        event.stopPropagation();
+
+        const confirmCallback = () => {
+            this.ModalServiceNg2.closeCurrentModal();
+            this.ComponentServiceNg2
+                .deleteInterfaceOperation(this.component, operation)
+                .subscribe(() => {
+                    const index = _.findIndex(this.operationList, el => el.uniqueId === operation.uniqueId);
+                    this.operationList.splice(index, 1);
+                    this.component.interfaceOperations = this.operationList;
+                });
+        }
+
+        this.modalInstance = this.ModalServiceNg2.createActionModal(
+            operation.operationType,
+            'Are you sure you want to delete this operation?',
+            'Delete',
+            confirmCallback,
+            'Cancel',
+        );
+
+        this.modalInstance.instance.open();
+    }
+
+    private createOperation = (operation: OperationModel): any => {
+        this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: CreateOperationResponse) => {
+            this.openOperation = null;
+
+            const workflowId = response.artifactUUID;
+            const operationId = response.uniqueId;
+            const resourceId = this.component.uuid;
+
+            const queryParams = {
+                id: workflowId,
+                operationID: operationId,
+                uuid: resourceId,
+                displayMode: 'create',
+            };
+
+            this.$state.go('workspace.plugins', {
+                path: 'workflowDesigner',
+                queryParams
+            });
+
+        });
+    }
+
+    private updateOperation = (operation: OperationModel): any => {
+        this.ComponentServiceNg2.updateInterfaceOperation(this.component, operation).subscribe(newOperation => {
+            this.openOperation = null;
+            const index = _.findIndex(this.operationList, el => el.uniqueId === operation.uniqueId);
+            this.operationList.splice(index, 1, newOperation);
+            this.component.interfaceOperations = this.operationList;
+        });
+    }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html
new file mode 100644 (file)
index 0000000..9e47cd5
--- /dev/null
@@ -0,0 +1,59 @@
+<div class="operation-creator">
+    <form class="w-sdc-form">
+
+        <div class="side-by-side">
+            <div class="i-sdc-form-item">
+                <label class="i-sdc-form-label">Operation Type</label>
+                <input
+                    type="text"
+                    data-tests-id="operationType"
+                    name="operationType"
+                    [(ngModel)]="operation.operationType"
+                    [attr.maxLength]="200"
+                    [disabled]="isEditMode" />
+            </div>
+
+            <div class="i-sdc-form-item" >
+                <label class="i-sdc-form-label">Description</label>
+                <input
+                    type="text"
+                    data-tests-id="operationDescription"
+                    name="description"
+                    [(ngModel)]="operation.description"
+                    [attr.maxLength]="200" />
+            </div>
+        </div>
+
+        <div class="separator-buttons">
+            <span class="input-param-title">Input Parameters</span>
+            <a
+                class="add-param-link"
+                data-tests-id="addInputParameter"
+                [ngClass]="{'disabled':!isAddAllowed()}"
+                (click)="addParam()">
+                Add Input Parameter
+            </a>
+        </div>
+
+        <div class="generic-table">
+            <div class="header-row table-row">
+                <span class="cell header-cell">Name</span>
+                <span class="cell header-cell">Property Name</span>
+                <span class="cell header-cell"></span>
+            </div>
+
+            <div class="empty-msg data-row" *ngIf="inputParams.length === 0">
+                No data to display.
+            </div>
+
+            <param-row
+                *ngFor="let param of inputParams; let idx=index"
+                class="data-row"
+                [param]="param"
+                [inputProps]="inputProperties"
+                [onRemoveParam]="onRemoveParam">
+            </param-row>
+        </div>
+
+    </form>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less
new file mode 100644 (file)
index 0000000..289dd5b
--- /dev/null
@@ -0,0 +1,69 @@
+@import '../../../../../assets/styles/variables.less';
+
+.operation-creator {
+    font-family: @font-opensans-regular;
+    user-select: none;
+    padding-top: 12px;
+    padding-bottom: 20px;
+
+    .i-sdc-form-label {
+        font-size: 12px;
+    }
+
+    .w-sdc-form .i-sdc-form-item {
+        margin-bottom: 15px;
+    }
+
+    .side-by-side {
+        display: flex;
+
+        .i-sdc-form-item {
+            flex-basis: 100%;
+
+            &:first-child {
+                flex-basis: 40%;
+                margin-right: 10px;
+            }
+        }
+    }
+
+    .input-param-title {
+        font-size: 16px;
+        text-transform: uppercase;
+    }
+
+    .separator-buttons {
+        margin: 10px 0;
+        display: flex;
+        justify-content: space-between;
+
+        .add-param-link {
+            &:not(.disabled):hover {
+                cursor: pointer;
+            }
+        }
+    }
+
+    .generic-table {
+        max-height: 233px;
+
+        .header-row .header-cell {
+            &:last-child {
+                padding: 0;
+            }
+        }
+
+        .data-row {
+            &.empty-msg {
+                padding: 6px 14px;
+            }
+        }
+
+        /deep/ .cell {
+            &:last-child {
+                min-width: 40px;
+            }
+        }
+
+    }
+}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts
new file mode 100644 (file)
index 0000000..cc7b5fe
--- /dev/null
@@ -0,0 +1,68 @@
+import * as _ from "lodash";
+import {Component} from '@angular/core';
+import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
+import {InputModel, OperationModel, OperationParam} from 'app/models';
+
+@Component({
+    selector: 'operation-creator',
+    templateUrl: './operation-creator.component.html',
+    styleUrls:['./operation-creator.component.less'],
+})
+
+export class OperationCreatorComponent {
+
+    inputProperties: Array<DropdownValue>;
+    input: any;
+    inputParams: Array<OperationParam> = [];
+    operation: OperationModel;
+    isEditMode: boolean = false;
+
+    ngOnInit() {
+        this.operation = new OperationModel(this.input.operation || {});
+
+        if (this.input.operation) {
+            let {inputParams} = this.input.operation;
+
+            if (inputParams) {
+                _.forEach(inputParams.listToscaDataDefinition, (input: OperationParam) => {
+                    this.addParam(input);
+                });
+            }
+
+            if (this.input.operation.uniqueId) {
+                this.isEditMode = true;
+            }
+        }
+
+        this.inputProperties = _.map(this.input.inputProperties,
+            (input: InputModel) => new DropdownValue(input.uniqueId, input.name)
+        );
+    }
+
+    addParam(param?: OperationParam): void {
+        this.inputParams.push(new OperationParam(param));
+    }
+
+    isAddAllowed(): boolean {
+        if (this.inputParams.length === 0) {
+            return true;
+        }
+
+        const {paramId, paramName} = _.last(this.inputParams);
+        return paramId && paramName.length > 0;
+    }
+
+    onRemoveParam = (param: OperationParam): void  => {
+        let index = _.indexOf(this.inputParams, param);
+        this.inputParams.splice(index, 1);
+    }
+
+    createInputParamList(): void {
+        this.operation.createInputParamsList(this.inputParams);
+    }
+
+    checkFormValidForSubmit(): boolean {
+        return this.operation.operationType && this.operation.operationType.length > 0 && this.isAddAllowed();
+    }
+
+}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts
new file mode 100644 (file)
index 0000000..9128e74
--- /dev/null
@@ -0,0 +1,27 @@
+import {NgModule} from "@angular/core";
+import {CommonModule} from "@angular/common";
+import {OperationCreatorComponent} from "./operation-creator.component";
+import {FormsModule} from "@angular/forms";
+import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module";
+import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
+import {ParamRowComponent} from './param-row/param-row.component';
+
+@NgModule({
+    declarations: [
+        OperationCreatorComponent,
+        ParamRowComponent
+    ],
+    imports: [
+        CommonModule,
+        FormsModule,
+        FormElementsModule,
+        UiElementsModule
+    ],
+    exports: [],
+    entryComponents: [
+        OperationCreatorComponent
+    ],
+    providers: []
+})
+
+export class OperationCreatorModule {}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html
new file mode 100644 (file)
index 0000000..86d7628
--- /dev/null
@@ -0,0 +1,21 @@
+<div class="cell">
+    <input
+        type="text"
+        data-tests-id="inputParamName"
+        [(ngModel)]="param.paramName" />
+</div>
+
+<ui-element-dropdown
+    class="cell"
+    data-tests-id="inputParamProperty"
+    [values]="inputProps"
+    [(value)]="param.paramId">
+</ui-element-dropdown>
+
+<div class="cell remove">
+    <span
+        class="sprite-new delete-item-icon"
+        data-tests-id="removeInputParam"
+        (click)="onRemoveParam(param)">
+    </span>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less
new file mode 100644 (file)
index 0000000..9abd7c7
--- /dev/null
@@ -0,0 +1,29 @@
+@import '../../../../../../assets/styles/variables.less';
+
+.remove {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .delete-item-icon {
+        &:hover {
+            cursor: pointer;
+        }
+    }
+}
+
+
+.cell {
+    padding: 0;
+
+    /deep/ select {
+        height: 30px;
+        border: none;
+    }
+
+    input {
+        height: 30px;
+        border: none;
+        padding-left: 10px;
+    }
+}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts
new file mode 100644 (file)
index 0000000..01e0629
--- /dev/null
@@ -0,0 +1,15 @@
+import {Component, Input} from '@angular/core';
+import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
+import {OperationParam} from 'app/models';
+
+@Component({
+    selector: 'param-row',
+    templateUrl: './param-row.component.html',
+    styleUrls: ['./param-row.component.less']
+})
+
+export class ParamRowComponent {
+    @Input() param: OperationParam;
+    @Input() inputProps: Array<DropdownValue>;
+    @Input() onRemoveParam: Function;
+}
index 9c3f78a..381995d 100644 (file)
@@ -24,7 +24,7 @@ import {Observable} from 'rxjs/Observable';
 import 'rxjs/add/operator/map';
 import 'rxjs/add/operator/toPromise';
 import {Response, URLSearchParams} from '@angular/http';
-import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData} from "app/models";
+import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, OperationModel, CreateOperationResponse} from "app/models";
 import {COMPONENT_FIELDS} from "app/utils";
 import {ComponentGenericResponse} from "../responses/component-generic-response";
 import {InstanceBePropertiesMap} from "../../../models/properties-inputs/property-fe-map";
@@ -114,6 +114,48 @@ export class ComponentServiceNg2 {
         return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_PROPERTIES]);
     }
 
+    getInterfaceOperations(component:Component):Observable<ComponentGenericResponse> {
+        return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INTERFACE_OPERATIONS]);
+    }
+
+    getInterfaceOperation(component:Component, operation:OperationModel):Observable<OperationModel> {
+        return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId)
+            .map((res:Response) => {
+                return res.json();
+            });
+    }
+
+    createInterfaceOperation(component:Component, operation:OperationModel):Observable<CreateOperationResponse> {
+        const operationList = {
+            'interfaceOperations': {
+                [operation.operationType]: operation
+            }
+        };
+        return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList)
+            .map((res:Response) => {
+                return res.json();
+            });
+    }
+
+    updateInterfaceOperation(component:Component, operation:OperationModel):Observable<CreateOperationResponse> {
+        const operationList = {
+            'interfaceOperations': {
+                [operation.operationType]: operation
+            }
+        };
+        return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList)
+            .map((res:Response) => {
+                return res.json();
+            });
+    }
+
+    deleteInterfaceOperation(component:Component, operation:OperationModel):Observable<OperationModel> {
+        return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId)
+            .map((res:Response) => {
+                return res.json();
+            });
+    }
+
     getCapabilitiesAndRequirements(componentType: string, componentId:string):Observable<ComponentGenericResponse> {
         return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_REQUIREMENTS, COMPONENT_FIELDS.COMPONENT_CAPABILITIES]);
     }
index e7c88a0..5036a10 100644 (file)
@@ -22,7 +22,7 @@
  * Created by ob0695 on 4/18/2017.
  */
 
-import { ArtifactGroupModel, PropertyModel, PropertiesGroup, AttributeModel, AttributesGroup, ComponentInstance,
+import { ArtifactGroupModel, PropertyModel, PropertiesGroup, AttributeModel, AttributesGroup, ComponentInstance, OperationModel,
     InputBEModel, Module, ComponentMetadata, RelationshipModel, RequirementsGroup, CapabilitiesGroup,InputFEModel} from "app/models";
 import {CommonUtils} from "app/utils";
 import {Serializable} from "../utils/serializable";
@@ -47,6 +47,7 @@ export class ComponentGenericResponse  implements Serializable<ComponentGenericR
     public policies:Array<PolicyInstance>;
     public groups:Array<Module>;
     public interfaces:any;
+    public interfaceOperations:Array<OperationModel>;
     public additionalInformation:any;
     public derivedList:Array<any>;
 
@@ -88,6 +89,9 @@ export class ComponentGenericResponse  implements Serializable<ComponentGenericR
         if(response.toscaArtifacts) {
             this.toscaArtifacts = new ArtifactGroupModel(response.toscaArtifacts);
         }
+        if(response.interfaces) {
+            this.interfaceOperations = CommonUtils.initInterfaceOperations(response.interfaces);
+        }
         if(response.metadata) {
             this.metadata = new ComponentMetadata().deserialize(response.metadata);
         }
index 9717794..1868550 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 import * as _ from "lodash";
-import {Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel} from "../models";
+import {Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel, OperationModel} from "../models";
 import {ComponentInstanceFactory} from "./component-instance-factory";
 import {InputBEModel, PropertyBEModel, RelationshipModel} from "app/models";
 import { PolicyInstance } from "app/models/graph/zones/policy-instance";
@@ -126,5 +126,37 @@ export class CommonUtils {
 
         return policies;
     }
+
+    static initInterfaceOperations(interfaces: any): Array<OperationModel> {
+
+        return _.reduce(interfaces, (acc, interf: any) => {
+
+            return acc.concat(
+                _.map(interf.operations,
+                    ({description, name, uniqueId, inputs, outputs}) => {
+                        const operation = new OperationModel({
+                            description,
+                            operationType: name,
+                            uniqueId
+                        });
+                        if (inputs) {
+                            const inputParams = _.map(inputs.listToscaDataDefinition, (input:any) => {
+                                return {paramName: input.name, paramId: input.inputId};
+                            });
+                            operation.createInputParamsList(inputParams);
+                        }
+                        if (outputs) {
+                            const outputParams = _.map(outputs.listToscaDataDefinition, (output:any) => {
+                                return {paramName: output.name, paramId: output.outputId};
+                            });
+                            operation.createOutputParamsList(outputParams);
+                        }
+                        return operation;
+                    }
+                )
+            );
+
+        }, []);
+    }
 }
 
index c860342..9665999 100644 (file)
@@ -235,6 +235,7 @@ export class States {
     public static WORKSPACE_INFORMATION_ARTIFACTS = 'workspace.information_artifacts';
     public static WORKSPACE_TOSCA_ARTIFACTS = 'workspace.tosca_artifacts';
     public static WORKSPACE_COMPOSITION = 'workspace.composition';
+    public static WORKSPACE_INTERFACE_OPERATION = 'workspace.interface_operation';
     public static WORKSPACE_NETWORK_CALL_FLOW = 'workspace.network_call_flow';
     public static WORKSPACE_MANAGEMENT_WORKFLOW = 'workspace.management_workflow';
     public static WORKSPACE_DEPLOYMENT = 'workspace.deployment';
@@ -318,7 +319,7 @@ export class COMPONENT_FIELDS {
     static COMPONENT_TOSCA_ARTIFACTS = "toscaArtifacts";
     static COMPONENT_POLICIES = "policies";
     static COMPONENT_GROUPS = "groups";
-
+    static COMPONENT_INTERFACE_OPERATIONS = "interfaces";
 }
 
 export class SERVICE_FIELDS {
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation-view-model.ts
new file mode 100644 (file)
index 0000000..1d42943
--- /dev/null
@@ -0,0 +1,15 @@
+'use strict';
+
+import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
+
+export interface IInterfaceOperationViewModelScope extends IWorkspaceViewModelScope {};
+
+export class InterfaceOperationViewModel {
+
+    static '$inject' = [
+        '$scope'
+    ];
+
+    constructor(private $scope: IInterfaceOperationViewModelScope) {}
+
+}
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation-view.html b/catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation-view.html
new file mode 100644 (file)
index 0000000..b83c097
--- /dev/null
@@ -0,0 +1,3 @@
+<div class="workspace-interface-operation">
+    <ng2-interface-operation [component]="component" [readonly]="isViewMode() || !isDesigner()"></ng2-interface-operation>
+</div>
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation.less b/catalog-ui/src/app/view-models/workspace/tabs/interface-operation/interface-operation.less
new file mode 100644 (file)
index 0000000..abfe3f9
--- /dev/null
@@ -0,0 +1,6 @@
+.workspace-interface-operation {
+    width: 100%;
+    display: inline-block;
+    top: -26px;
+    position: relative;
+}
index 3e0fb44..1aff3db 100644 (file)
@@ -48,11 +48,15 @@ export class PluginsContextViewModel {
             uuid: this.$scope.component.uuid,
             lifecycleState: this.$scope.component.lifecycleState,
             isOwner: this.$scope.component.lastUpdaterUserId === this.$scope.user.userId,
-            version: this.$scope.component.version ,
+            version: this.$scope.component.version,
             parentUrl: window.location.origin,
             eventsClientId: this.$scope.plugin.pluginId
         };
 
+        if (this.$stateParams.queryParams) {
+            _.assign(this.$scope.queryParams, this.$stateParams.queryParams);
+        }
+
         this.$scope.onLoadingDone = (plugin: Plugin) => {
             if (plugin.pluginId == this.$scope.plugin.pluginId) {
                 this.$scope.isLoading = false;
index 6eabc7e..638b28e 100644 (file)
 @import '../../app/view-models/workspace/tabs/properties/properties.less';
 @import '../../app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities.less';
 @import '../../app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts.less';
+@import '../../app/view-models/workspace/tabs/interface-operation/interface-operation.less';
 @import '../../app/view-models/workspace/workspace.less';
 @import '../../app/view-models/workspace/tabs/plugins/plugins-context.less';