Provide UI page for interface assignment in service for VFC instances 17/135817/7
authorimamSidero <imam.hussain@est.tech>
Thu, 24 Aug 2023 10:48:06 +0000 (11:48 +0100)
committerMichael Morris <michael.morris@est.tech>
Mon, 4 Sep 2023 14:21:30 +0000 (14:21 +0000)
Support for interface assignment page for VFC instances is provided in service instances

Issue-ID: SDC-4602
Signed-off-by: Imam hussain <imam.hussain@est.tech>
Change-Id: I4cabef02db381278d37d21da981d3ec4c04e5967

catalog-ui/configurations/menu.js
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.module.ts
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.html
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.less
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.ts
catalog-ui/src/app/ng2/services/component-services/component.service.ts

index a5114d8..54c1b63 100644 (file)
@@ -297,6 +297,7 @@ const SDC_MENU_CONFIG = {
             {"text": "Network Call Flow ", "action": "onMenuItemPressed", "state": "workspace.network_call_flow"},
             {"text": "Distribution","action": "onMenuItemPressed","state": "workspace.distribution","disabledRoles": ["ADMIN"]},
             {"text": "Deployment", "action": "onMenuItemPressed", "state": "workspace.deployment"},
+            {"text": "Interfaces Assignment", "action": "onMenuItemPressed", "state": "workspace.interface-definition"},
             {"text": "Properties Assignment", "action": "onMenuItemPressed", "state": "workspace.properties_assignment"},
             {"text": "Attributes & Outputs", "action": "onMenuItemPressed", "state": "workspace.attributes_outputs"}
         ],
index 27a7f11..ea6279e 100644 (file)
@@ -24,6 +24,7 @@ import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
 import {TranslateModule} from "app/ng2/shared/translator/translate.module";
 import { SdcUiComponentsModule } from 'onap-ui-angular';
 import {InterfaceDefinitionComponent} from "./interface-definition.page.component";
+import {HierarchyNavigationModule} from "../../components/logic/hierarchy-navigtion/hierarchy-navigation.module";
 import {InterfaceOperationHandlerModule} from "../composition/interface-operatons/operation-creator/interface-operation-handler.module";
 
 @NgModule({
@@ -34,6 +35,7 @@ import {InterfaceOperationHandlerModule} from "../composition/interface-operaton
         CommonModule,
         SdcUiComponentsModule,
         UiElementsModule,
+        HierarchyNavigationModule,
         TranslateModule,
         InterfaceOperationHandlerModule
     ],
index bf36df0..15fe65a 100644 (file)
 -->
 <div class="interface-definition">
   <loader [display]="isLoading" [size]="'large'" [relative]="true"></loader>
-  <div *ngIf="isInterfaceListEmpty()">
-    <div class="interface-empty-msg">
-      <div>{{ 'INTERFACE_DATA_EMPTY' | translate }}</div>
+  <div class="left-column">
+    <div *ngIf="isInterfaceListEmpty()">
+      <div class="interface-empty-msg">
+        <div>{{ 'INTERFACE_DATA_EMPTY' | translate }}</div>
+      </div>
     </div>
-  </div>
-  <div
-      class="top-add-btn add-btn"
-      [ngClass]="{'disabled': readonly}"
-      data-tests-id="add-operation"
-      (click)="onSelectInterfaceOperation(undefined, undefined)">
+    <div class="top-add-btn add-btn" [ngClass]="{'disabled': readonly}" *ngIf="!component.isService()" data-tests-id="add-operation" (click)="onSelectInterfaceOperation(undefined, undefined)">
     {{ 'INTERFACE_ADD_OPERATION' | translate }}
-  </div>
-  <div class="operation-list">
-    <div *ngIf="!isInterfaceListEmpty()">
-      <div class="expand-collapse" *ngIf="isOperationListEmpty()">
-        <a class="link"
-           [ngClass]="{'disabled': isAllExpanded()}"
-           (click)="collapseAll(false)">{{ 'INTERFACE_EXPAND_ALL' | translate }}
-        </a> |
-        <a class="link"
-           [ngClass]="{'disabled': isAllCollapsed()}"
-           (click)="collapseAll()">
-          {{ 'INTERFACE_COLLAPSE_ALL' | translate }}
-        </a>
-      </div>
-
-      <div class="interface-row" *ngFor="let interface1 of interfaces">
-        <div class="interface-accordion" (click)="interface1.toggleCollapse()">
-          <span
-              class="chevron-container"
-              [ngClass]="{'isCollapsed': interface1.isCollapsed}"
-              *ngIf="isOperationListEmpty()">
-              <svg-icon
-                  name="caret1-down-o"
-                  mode="primary"
-                  size="small">
-              </svg-icon>
-          </span>
-          <span class="interface-name">{{interface1.type}}</span>
+    </div>
+    <div class="operation-list">
+      <div *ngIf="!isInterfaceListEmpty()">
+        <div class="expand-collapse" *ngIf="isOperationListEmpty()">
+          <a class="link"
+            [ngClass]="{'disabled': isAllExpanded()}"
+            (click)="collapseAll(false)">{{ 'INTERFACE_EXPAND_ALL' | translate }}
+          </a> |
+          <a class="link"
+            [ngClass]="{'disabled': isAllCollapsed()}"
+            (click)="collapseAll()">
+            {{ 'INTERFACE_COLLAPSE_ALL' | translate }}
+          </a>
         </div>
 
-        <div class="generic-table" *ngIf="!interface1.isCollapsed && isOperationListEmpty()">
-          <div class="header-row table-row">
+        <div class="interface-row" *ngFor="let interface1 of interfaces">
+          <div class="interface-accordion" (click)="interface1.toggleCollapse()">
             <span
-                class="cell header-cell field-name header-name">
-                {{ 'INTERFACE_HEADER_NAME' | translate }}
+                class="chevron-container"
+                [ngClass]="{'isCollapsed': interface1.isCollapsed}"
+                *ngIf="isOperationListEmpty()">
+                <svg-icon
+                    name="caret1-down-o"
+                    mode="primary"
+                    size="small">
+                </svg-icon>
             </span>
-            <span class="cell header-cell field-description header-description">
-              {{ 'INTERFACE_HEADER_DESCRIPTION' | translate }}
-            </span>
-            <span class="cell field-delete" *ngIf="!readonly"><span class="delete-col-header"></span></span>
+            <span class="interface-name">{{interface1.type}}</span>
           </div>
-          <div class="data-row" *ngFor="let operation of interface1.operations">
-            <span class="cell field-name"
-                  (click)="onSelectInterfaceOperation(interface1, operation)">{{operation.name}}</span>
-            <span class="cell field-description" (click)="onSelectInterfaceOperation(interface1, operation)"
-                  [ngClass]="{'collapsed': operation.isCollapsed}">{{operation.getDescriptionEllipsis()}}
-              <span class="more-or-less link" (click)="operation.toggleCollapsed($event)">
-                  {{!operation.isEllipsis ? '' : operation.isCollapsed ? 'More' : 'Less'}}
+
+          <div class="generic-table" *ngIf="!interface1.isCollapsed && isOperationListEmpty()">
+            <div class="header-row table-row">
+              <span
+                  class="cell header-cell field-name header-name">
+                  {{ 'INTERFACE_HEADER_NAME' | translate }}
               </span>
-            </span>
-            <span class="cell field-delete" *ngIf="!readonly">
-              <button class="table-delete-btn" (click)="onRemoveOperation(operation)"></button>
-            </span>
+              <span class="cell header-cell field-description header-description">
+                {{ 'INTERFACE_HEADER_DESCRIPTION' | translate }}
+              </span>
+              <span class="cell field-delete" *ngIf="!readonly"><span class="delete-col-header"></span></span>
+            </div>
+            <div class="data-row" *ngFor="let operation of interface1.operations">
+              <span class="cell field-name"
+                    (click)="onSelectInterfaceOperation(interface1, operation)">{{operation.name}}</span>
+              <span class="cell field-description" (click)="onSelectInterfaceOperation(interface1, operation)"
+                    [ngClass]="{'collapsed': operation.isCollapsed}">{{operation.getDescriptionEllipsis()}}
+                <span class="more-or-less link" (click)="operation.toggleCollapsed($event)">
+                    {{!operation.isEllipsis ? '' : operation.isCollapsed ? 'More' : 'Less'}}
+                </span>
+              </span>
+              <span class="cell field-delete" *ngIf="!readonly">
+                <button class="table-delete-btn" (click)="onRemoveOperation(operation)"></button>
+              </span>
+            </div>
           </div>
         </div>
       </div>
     </div>
   </div>
+  <div class="right-column" *ngIf="component.isService()">
+    <div class="add-btn" [ngClass]="{'disabled': disableFlag}" data-tests-id="add-operation" (click)="onSelectInterfaceOperation(undefined, undefined)">
+      {{ 'INTERFACE_ADD_OPERATION' | translate }}
+    </div>
+    <tabs #hierarchyNavTabs tabStyle="simple-tabs" class="gray-border">
+        <tab tabTitle="Composition">
+            <div class="hierarchy-nav-container">
+                <loader [display]="loadingInstances" [size]="'medium'" [relative]="true" [loaderDelay]="500"></loader>
+                <div class="hierarchy-header white-sub-header">
+                    <span tooltip="{{component.name}}">{{component.name}}</span>
+                </div>
+                <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0">No data to display</div>
+                <hierarchy-navigation class="hierarchy-nav"
+                        (updateSelected)="onInstanceSelectedUpdate($event)"
+                        [displayData]="instancesNavigationData"
+                        [selectedItem]="selectedInstanceData?.uniqueId"
+                        [displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation>
+            </div>
+        </tab>
+    </tabs>
+  </div>
 </div>
index 464732e..67e2dae 100644 (file)
@@ -23,7 +23,9 @@
 @import '../../../../assets/styles/sprite-old.less';
 .interface-definition {
     font-size: 14px;
-
+    display:flex;
+    flex-direction:row;
+    height: 100%;
     .delete-col-header{
         .sprite;
         .sprite.e-sdc-small-icon-delete;
         padding: 14px;
     }
 
-    .top-add-btn {
-        position: relative;
-        top: -31px;
-        text-transform: uppercase;
-        font-size: 14px;
-        font-family: @font-opensans-medium;
-    }
-
     .link {
         color: @sdcui_color_blue;
         text-decoration: underline;
@@ -79,7 +73,7 @@
     .operation-list {
         border-top: 1px solid @main_color_o;
         padding-top: 5px;
-
+        
         .empty-list-container {
             width: 100%;
             display: flex;
 
         }
     }
+    .left-column {
+        flex: 1 0 500px;
+        position: relative;
+           min-width:930px;
+    }
+    .right-column {
+        display:flex;
+        flex:0 0 350px;
+        flex-direction:column;
+        margin: 0px 0 0 1em;
+        overflow-x:auto;
+        .add-btn{
+            align-self: flex-end;
+            margin-top: 10px;
+            margin-bottom: 19px;
+        }
+        /deep/ .tabs {
+            border-bottom: solid 1px #d0d0d0;
+        }
+
+        /deep/ .tab {
+            flex: none;
+            padding: 8px 20px 0;
+            font-size: 14px;
+            line-height:30px;
+            font-family: @font-opensans-regular;
+
+            &.active {
+                font-family: @font-opensans-medium;
+            }
+        }
+    }
+
+    .hierarchy-tabs {
+        flex: 0 0 40px;
+    }
+
+    .gray-border {
+        border: 1px solid #ddd;
+    }
+
+    /deep/ .white-sub-header {
+        background-color:  #fffefe;
+        box-shadow: 0px 1px 0.99px 0.01px rgba(34, 31, 31, 0.15);
+        border-bottom: #d2d2d2 solid 1px;
+        font-size:14px;
+        text-align:left;
+        flex:0 0 auto;
+        padding: 10px;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        overflow: hidden;
+        text-transform: uppercase;
+
+        &.hierarchy-header {
+            padding-left:20px;
+            &.selected {
+                background-color: #e6f6fb;
+            }
+        }
+
+    }
+
+    .hierarchy-header {
+
+        span{
+            text-overflow: ellipsis;
+            overflow: hidden;
+            white-space: nowrap;
+            max-width: 290px;
+        }
+    }
+
+    .hierarchy-nav {
+        flex:1;
+        overflow:auto;
+        display: grid;
+        margin-top: 1em;
+        margin-left: 1em;
+        font-size: 12px;
+        padding-top: 1em;
+    }
 }
index 90d6a6c..23c855e 100644 (file)
@@ -21,7 +21,7 @@
 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 {HierarchyDisplayOptions} from "../../components/logic/hierarchy-navigtion/hierarchy-display-options";
 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';
@@ -34,6 +34,7 @@ import {
     CapabilitiesGroup,
     InputBEModel,
     InterfaceModel,
+    ComponentInstance,
     ModalModel,
     OperationModel,
     WORKFLOW_ASSOCIATION_OPTIONS
@@ -49,6 +50,7 @@ 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';
+import { InstanceFeDetails } from 'app/models/instance-fe-details';
 
 export class UIOperationModel extends OperationModel {
     isCollapsed: boolean = true;
@@ -132,6 +134,13 @@ export class InterfaceDefinitionComponent {
     interfaces: UIInterfaceModel[];
     inputs: InputBEModel[];
 
+    instancesNavigationData = [];
+    instances: any = [];
+    loadingInstances: boolean = false;
+    selectedInstanceData: any = null;
+    hierarchyInstancesDisplayOptions: HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name', 'archived', null, 'iconClass');
+    disableFlag : boolean = true;
+
     deploymentArtifactsFilePath: Array<DropdownValue> = [];
 
     toscaArtifactTypes: Array<DropdownValue> = [];
@@ -148,6 +157,7 @@ export class InterfaceDefinitionComponent {
     enableWorkflowAssociation: boolean;
     workflowIsOnline: boolean;
     validImplementationProps:boolean = true;
+    serviceInterfaces: InterfaceModel[];
 
     @Input() component: IComponent;
     @Input() readonly: boolean;
@@ -176,21 +186,39 @@ export class InterfaceDefinitionComponent {
     ngOnInit(): void {
         this.isLoading = true;
         this.interfaces = [];
+        //this.disableFlag = this.readonly;
         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)
+            this.ComponentServiceNg2.getCapabilitiesAndRequirements(this.component.componentType, this.component.uniqueId),
+            this.componentServiceNg2.getComponentResourcePropertiesData(this.component)
         ).subscribe((response: any[]) => {
             const callback = (workflows) => {
                 this.isLoading = false;
+                this.serviceInterfaces = response[0].interfaces;
                 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;
+                this.instances = response[4].componentInstances;
+                const serviceInstance = new ComponentInstance();
+                serviceInstance.name = "SELF";
+                serviceInstance.uniqueId = this.component.uniqueId;
+                if (this.instances != null) {
+                    this.instances.unshift(serviceInstance);
+                } else {
+                    this.instances = [serviceInstance];
+                }
+                _.forEach(this.instances, (instance) => {
+                    this.instancesNavigationData.push(instance);
+                });
+                this.onInstanceSelectedUpdate(this.instancesNavigationData[0]);
+                this.loadingInstances = false;
+                
             };
             if (this.enableWorkflowAssociation && this.workflowIsOnline) {
                 this.WorkflowServiceNg2.getWorkflows().subscribe(
@@ -204,9 +232,55 @@ export class InterfaceDefinitionComponent {
                 callback([]);
             }
         });
+
         this.loadToscaArtifacts();
     }
 
+    onInstanceSelectedUpdate = (instance: any) => {
+        this.selectedInstanceData = instance;
+        if (instance.name != "SELF") {
+            this.disableFlag = true;
+            let newInterfaces : InterfaceModel[] = [];
+            if (instance.interfaces instanceof Array) {
+                instance.interfaces.forEach(result => {
+                    let interfaceObj = new InterfaceModel();
+                    interfaceObj.type = result.type;
+                    interfaceObj.uniqueId = result.uniqueId;
+                    if (result.operations instanceof Array) {
+                        interfaceObj.operations = result.operations;
+                    } else if (!_.isEmpty(result.operations)) {
+                        interfaceObj.operations = [];
+                        Object.keys(result.operations).forEach(name => {
+                            interfaceObj.operations.push(result.operations[name]);
+                        });
+                    }
+                    newInterfaces.push(interfaceObj);
+                });
+            } else {
+                Object.keys(instance.interfaces).forEach(key => {
+                    let obj = instance.interfaces[key];
+                    let interfaceObj = new InterfaceModel();
+                    interfaceObj.type = obj.type;
+                    interfaceObj.uniqueId = obj.uniqueId;
+                    if (obj.operations instanceof Array) {
+                        interfaceObj.operations = obj.operations;
+                    } else if (!_.isEmpty(obj.operations)) {
+                        interfaceObj.operations = [];
+                        Object.keys(obj.operations).forEach(name => {
+                            interfaceObj.operations.push(obj.operations[name]);
+                        });
+                    }
+                    newInterfaces.push(interfaceObj);
+                });
+            }
+            this.interfaces = newInterfaces.map((interf) => new UIInterfaceModel(interf));
+        } else {
+            //this.disableFlag = this.readonly;
+            this.interfaces = this.serviceInterfaces.map((interf) => new UIInterfaceModel(interf));
+        }
+        this.sortInterfaces();
+    }
+
     initInterfaces(interfaces: InterfaceModel[]): void {
         if (interfaces) {
             this.interfaces = interfaces.map((interf) => new UIInterfaceModel(interf));
@@ -222,6 +296,9 @@ export class InterfaceDefinitionComponent {
         if(this.readonly) {
             return disable;
         }
+        if (this.component.isService()) {
+            return disable;
+        }
     
         let selectedInterfaceOperation = this.modalInstance.instance.dynamicContent.instance.selectedInterfaceOperation;
         let isInterfaceOperation:boolean = !(typeof selectedInterfaceOperation == 'undefined' || _.isEmpty(selectedInterfaceOperation));
index 450e66e..eca70be 100644 (file)
@@ -114,7 +114,7 @@ export class ComponentServiceNg2 {
     }
 
     getComponentResourcePropertiesData(component: Component): Observable<ComponentGenericResponse> {
-        return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]);
+        return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_INTERFACE_OPERATIONS, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_NON_EXCLUDED_GROUPS]);
     }
 
     getComponentResourceAttributesData(component: Component): Observable<ComponentGenericResponse> {
@@ -157,6 +157,8 @@ export class ComponentServiceNg2 {
         return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INTERFACE_OPERATIONS]);
     }
 
+    
+
     getInterfaceOperation(component: Component, operation: OperationModel): Observable<OperationModel> {
         return this.http.get<OperationModel>(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId);
     }