View Interface definition on VFC 53/126653/8
authoraribeiro <anderson.ribeiro@est.tech>
Thu, 13 Jan 2022 14:33:25 +0000 (14:33 +0000)
committerMichael Morris <michael.morris@est.tech>
Wed, 26 Jan 2022 12:57:51 +0000 (12:57 +0000)
Add Interface support to VFC view UI

Issue-ID: SDC-3850
Signed-off-by: aribeiro <anderson.ribeiro@est.tech>
Change-Id: Icd195c939af39d40ae8c617e740323dd3e70fc15

26 files changed:
catalog-ui/configurations/menu.js
catalog-ui/src/app/app.ts
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/composition/interface-operatons/interface-operations.component.ts
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html
catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.module.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.html [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.less [new file with mode: 0644]
catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.ts [new file with mode: 0644]
catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts
catalog-ui/src/app/utils/constants.ts
catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view-model.ts [new file with mode: 0644]
catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view.html [new file with mode: 0644]
catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition.less [new file with mode: 0644]
catalog-ui/src/assets/languages/en_US.json
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ServiceTemplateDesignUiTests.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/flow/InterfaceDefinitionFlow.java [new file with mode: 0644]
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsTab.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceDefinitionOperationsModal.java [moved from integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsModal.java with 95% similarity]
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceDefinitionPage.java [new file with mode: 0644]

index beecfd1..38f504b 100644 (file)
@@ -228,6 +228,7 @@ const SDC_MENU_CONFIG = {
             {"text": "TOSCA Artifacts", "action": "onMenuItemPressed", "state": "workspace.tosca_artifacts"},
             {"text": "Properties", "action": "onMenuItemPressed", "state": "workspace.properties"},
             {"text": "Attributes", "action": "onMenuItemPressed", "state": "workspace.attributes"},
+            {"text": "Interfaces", "action": "onMenuItemPressed", "state": "workspace.interface-definition"},
             {"text": "Req. & Capabilities", "action": "onMenuItemPressed", "state": "workspace.reqAndCap"},
             {"text": "Activity Log", "action": "onMenuItemPressed", "state": "workspace.activity_log"}
         ],
index 87930fd..ffa4389 100644 (file)
@@ -511,6 +511,18 @@ ng1appModule.config([
         }
     );
 
+    $stateProvider.state(
+        States.WORKSPACE_INTERFACE_DEFINITION, {
+          url: 'interfaceDefinition',
+          parent: 'workspace',
+          controller: viewModelsModuleName + '.InterfaceDefinitionViewModel',
+          templateUrl: './view-models/workspace/tabs/interface-definition/interface-definition-view.html',
+          data: {
+            bodyClass: 'interfaceDefinition'
+          }
+        }
+    );
+
     $stateProvider.state(
         'workspace.plugins', {
           url: 'plugins/*path',
index 270a764..7719f73 100644 (file)
@@ -96,6 +96,7 @@ import {DeploymentArtifactsPageComponent} from "../ng2/pages/workspace/deploymen
 import {ReqAndCapabilitiesComponent} from "../ng2/pages/workspace/req-and-capabilities/req-and-capabilities.component";
 import {DistributionComponent} from '../ng2/pages/workspace/disribution/distribution.component';
 import {AttributesOutputsComponent} from "../ng2/pages/attributes-outputs/attributes-outputs.page.component";
+import {InterfaceDefinitionComponent} from "../ng2/pages/interface-definition/interface-definition.page.component";
 
 let moduleName: string = 'Sdc.Directives';
 let directiveModule: ng.IModule = angular.module(moduleName, []);
@@ -245,6 +246,12 @@ directiveModule.directive('interfaceOperation', downgradeComponent({
   outputs: []
 }) as angular.IDirectiveFactory);
 
+directiveModule.directive('interfaceDefinition', downgradeComponent({
+  component: InterfaceDefinitionComponent,
+  inputs: ['component', 'readonly'],
+  outputs: []
+}) as angular.IDirectiveFactory);
+
 directiveModule.directive('ng2MultilineEllipsis', downgradeComponent({
   component: MultilineEllipsisComponent,
   inputs: ['lines', 'lineHeight', 'className'],
@@ -309,6 +316,7 @@ directiveModule.directive('informationArtifactPage', downgradeComponent({
   inputs: [],
   outputs: []
 }) as angular.IDirectiveFactory);
+
 directiveModule.directive('deploymentArtifactPage', downgradeComponent({
   component: DeploymentArtifactsPageComponent,
   inputs: [],
index 5b8fc59..dd08135 100644 (file)
@@ -36,6 +36,7 @@ import {PropertiesViewModel} from "../view-models/workspace/tabs/properties/prop
 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 {InterfaceDefinitionViewModel} from "../view-models/workspace/tabs/interface-definition/interface-definition-view-model";
 let moduleName:string = 'Sdc.ViewModels';
 let viewModelModule:ng.IModule = angular.module(moduleName, []);
 
@@ -59,4 +60,5 @@ viewModelModule
   .controller(moduleName + '.PropertiesViewModel', PropertiesViewModel)
   .controller(moduleName + '.ManagementWorkflowViewModel', ManagementWorkflowViewModel)
   .controller(moduleName + '.InterfaceOperationViewModel', InterfaceOperationViewModel)
+  .controller(moduleName + '.InterfaceDefinitionViewModel', InterfaceDefinitionViewModel)
   .controller(moduleName + '.NetworkCallFlowViewModel', NetworkCallFlowViewModel);
index 5b12ae9..f6ba919 100644 (file)
@@ -102,6 +102,7 @@ import {AttributesOutputsModule} from "./pages/attributes-outputs/attributes-out
 import { ElementService } from "./services/element.service";
 import { ModelService } from "./services/model.service";
 import {ToscaArtifactService} from "./services/tosca-artifact.service";
+import {InterfaceDefinitionModule} from "./pages/interface-definition/interface-definition.module";
 
 
 declare const __ENV__: string;
@@ -156,8 +157,9 @@ export function configServiceFactory(config: ConfigService, authService: Authent
     PluginFrameModule,
     PluginsModule,
     InterfaceOperationModule,
+    InterfaceDefinitionModule,
     OperationCreatorModule,
-        InterfaceOperationHandlerModule,
+    InterfaceOperationHandlerModule,
     ServicePathCreatorModule,
     ServicePathsListModule,
     ServicePathSelectorModule,
index 2cc91a9..60d6678 100644 (file)
@@ -227,7 +227,8 @@ export class InterfaceOperationsComponent {
           toscaArtifactTypes: this.toscaArtifactTypes,
           selectedInterface: interfaceModel,
           selectedInterfaceOperation: operation,
-          validityChangedCallback: this.enableOrDisableSaveButton
+          validityChangedCallback: this.enableOrDisableSaveButton,
+          isViewOnly: false
         }
     );
     this.modalInstance.instance.open();
index 428c4cd..6dec416 100644 (file)
@@ -28,7 +28,7 @@
                 <sdc-input
                     label="{{ 'OPERATION_INTERFACE_TYPE' | translate }}"
                     [(value)]="interfaceType"
-                    [disabled]="true">
+                    [disabled]=isViewOnly>
                 </sdc-input>
             </div>
 
@@ -36,7 +36,7 @@
                 <sdc-input
                     label="{{ 'OPERATION_NAME' | translate }}"
                     [(value)]="operationToUpdate.name"
-                    [disabled]="true">
+                    [disabled]=isViewOnly>
                 </sdc-input>
             </div>
         </div>
@@ -46,7 +46,8 @@
                 label="{{'OPERATION_DESCRIPTION' | translate}}"
                 [(value)]="operationToUpdate.description"
                 testId="interface-operation-description"
-                (valueChange)="onDescriptionChange($event)">
+                (valueChange)="onDescriptionChange($event)"
+                [disabled]=isViewOnly>
             </sdc-input>
         </div>
 
@@ -55,7 +56,8 @@
             <div class="form-item">
                 <checkbox [label]="'Add Artifact To Implementation'"
                           [(checked)]="enableAddArtifactImplementation"
-                          (checkedChange)="onMarkToAddArtifactToImplementation($event)">
+                          (checkedChange)="onMarkToAddArtifactToImplementation($event)"
+                          [disabled]=isViewOnly>
                 </checkbox>
             </div>
             <div class="form-item" *ngIf="!enableAddArtifactImplementation">
@@ -63,7 +65,8 @@
                     label="{{'INTERFACE_OPERATION_IMPLEMENTATION_NAME' | translate}}"
                     testId="interface-operation-implementation-name"
                     [(value)]="artifactName"
-                    (valueChange)="onImplementationNameChange($event)">
+                    (valueChange)="onImplementationNameChange($event)"
+                    [disabled]=isViewOnly>
                 </sdc-input>
             </div>
 
@@ -76,7 +79,8 @@
                         [selectedOption]="toscaArtifactTypeSelected"
                         placeHolder="{{toscaArtifactTypeSelected != undefined ? toscaArtifactTypeSelected : 'Select...'}}"
                         (changed)="onSelectToscaArtifactType($event)"
-                        [options]="toscaArtifactTypes">
+                        [options]="toscaArtifactTypes"
+                        [disabled]=isViewOnly>
                     </sdc-dropdown>
                 </div>
                 <div class="form-item" *ngIf="toscaArtifactTypeSelected && enableAddArtifactImplementation">
@@ -85,7 +89,8 @@
                         data-tests-id="artifactFile"
                         [(value)]="artifactName"
                         [required]="true"
-                        (valueChange)="onArtifactFileChange($event)">
+                        (valueChange)="onArtifactFileChange($event)"
+                        [disabled]=isViewOnly>
                     </sdc-input>
                 </div>
                 <div class="form-item">
@@ -93,7 +98,8 @@
                         label="{{ 'ARTIFACT_VERSION' | translate }}"
                         data-tests-id="artifactVersion"
                         [(value)]="artifactVersion"
-                        (valueChange)="onArtifactVersionChange($event)">
+                        (valueChange)="onArtifactVersionChange($event)"
+                        [disabled]=isViewOnly>
                     </sdc-input>
                 </div>
             </div>
         <div class="separator-buttons">
             <tab tabTitle="Inputs"></tab>
             <a class="add-param-link add-btn"
-               [ngClass]="{'disabled': readonly}"
+               [ngClass]="{'disabled': readonly || isViewOnly}"
                (click)="onAddInput()">{{'OPERATION_ADD_INPUT' | translate}}
             </a>
         </div>
index 6e4ae45..1099391 100644 (file)
@@ -49,6 +49,7 @@ export class InterfaceOperationHandlerComponent {
         selectedInterface: UIInterfaceModel;
         selectedInterfaceOperation: InterfaceOperationModel;
         validityChangedCallback: Function;
+        isViewOnly: boolean;
     };
 
     interfaceType: string;
@@ -60,6 +61,7 @@ export class InterfaceOperationHandlerComponent {
     properties: Array<PropertyParamRowComponent> = [];
     isLoading: boolean = false;
     readonly: boolean;
+    isViewOnly: boolean;
 
     toscaArtifactTypeSelected: string;
     toscaArtifactTypeProperties: Array<PropertyBEModel> = [];
@@ -70,6 +72,7 @@ export class InterfaceOperationHandlerComponent {
     propertyValueValid: boolean = true;
 
     ngOnInit() {
+        this.isViewOnly = this.input.isViewOnly;
         this.interfaceType = this.input.selectedInterface.displayType();
         this.operationToUpdate = new InterfaceOperationModel(this.input.selectedInterfaceOperation);
         this.operationToUpdate.interfaceId = this.input.selectedInterface.uniqueId;
diff --git a/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.module.ts b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.module.ts
new file mode 100644 (file)
index 0000000..27a7f11
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+*  Copyright (C) 2022 Nordix Foundation. All rights reserved.
+*  ================================================================================
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*  SPDX-License-Identifier: Apache-2.0
+*  ============LICENSE_END=========================================================
+*/
+import {NgModule} from "@angular/core";
+import {CommonModule} from "@angular/common";
+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 {InterfaceOperationHandlerModule} from "../composition/interface-operatons/operation-creator/interface-operation-handler.module";
+
+@NgModule({
+    declarations: [
+        InterfaceDefinitionComponent,
+    ],
+    imports: [
+        CommonModule,
+        SdcUiComponentsModule,
+        UiElementsModule,
+        TranslateModule,
+        InterfaceOperationHandlerModule
+    ],
+    exports: [],
+    entryComponents: [
+        InterfaceDefinitionComponent
+    ],
+    providers: []
+})
+
+export class InterfaceDefinitionModule {}
diff --git a/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.html b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.html
new file mode 100644 (file)
index 0000000..25ccf11
--- /dev/null
@@ -0,0 +1,85 @@
+<!--
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+-->
+<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>
+  </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 interface of interfaces">
+        <div class="interface-accordion" (click)="interface.toggleCollapse()">
+          <span
+              class="chevron-container"
+              [ngClass]="{'isCollapsed': interface.isCollapsed}"
+              *ngIf="isOperationListEmpty()">
+              <svg-icon
+                  name="caret1-down-o"
+                  mode="primary"
+                  size="small">
+              </svg-icon>
+          </span>
+          <span class="interface-name">{{interface.type}}</span>
+        </div>
+
+        <div class="generic-table" *ngIf="!interface.isCollapsed && isOperationListEmpty()">
+          <div class="header-row table-row">
+            <span
+                class="cell header-cell field-name header-name">
+                {{ 'INTERFACE_HEADER_NAME' | translate }}
+            </span>
+            <span class="cell header-cell field-description header-description">
+              {{ 'INTERFACE_HEADER_DESCRIPTION' | translate }}
+            </span>
+          </div>
+
+          <div class="data-row" *ngFor="let operation of interface.operations"
+               (click)="onSelectInterfaceOperation(interface, operation)">
+            <span
+                class="cell field-name">
+                {{operation.name}}
+            </span>
+            <span class="cell field-description"
+                  [ngClass]="{'collapsed': operation.isCollapsed}">
+              {{operation.getDescriptionEllipsis()}}
+              <span class="more-or-less link" (click)="operation.toggleCollapsed($event)">
+                {{!operation.isEllipsis ? '' : operation.isCollapsed ? 'More' : 'Less'}}
+              </span>
+            </span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.less b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.less
new file mode 100644 (file)
index 0000000..2b76c8c
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+*  Copyright (C) 2022 Nordix Foundation. All rights reserved.
+*  ================================================================================
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*  SPDX-License-Identifier: Apache-2.0
+*  ============LICENSE_END=========================================================
+*/
+@import '../../../../assets/styles/variables.less';
+@import '../../../../assets/styles/override.less';
+
+.interface-definition {
+    font-size: 14px;
+
+    .interface-empty-msg {
+        .bold-message {
+            font-family: @font-opensans-bold;
+        }
+
+        :first-child {
+            &:not(:only-child) {
+                margin: 6px 0;
+            }
+        }
+
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        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;
+        font-family: @font-opensans-regular;
+
+        &:not(.disabled) {
+            &:not(.empty-list-add-btn) {
+                &:hover {
+                    color: @sdcui_color_dark-blue;
+                    cursor: pointer;
+                }
+            }
+        }
+    }
+
+    .operation-list {
+        border-top: 1px solid @main_color_o;
+        padding-top: 5px;
+
+        .empty-list-container {
+            width: 100%;
+            display: flex;
+            justify-content: center;
+
+            .empty-list-add-btn {
+                display: flex;
+                flex-direction: column;
+                justify-content: center;
+                align-items: center;
+
+                border: 1px solid @main_color_o;
+                margin-top: 50px;
+
+                height: 229px;
+                width: 480px;
+
+                &.disabled {
+                    pointer-events: none;
+                }
+
+                &:hover {
+                    &:not(.disabled) {
+                        border: 1px solid @sdcui_color_blue;
+                        cursor: pointer;
+                    }
+                }
+
+                .button-text {
+                    margin-top: 9px;
+                    font-family: @font-opensans-medium;
+                    font-size: 16px;
+                    text-transform: uppercase;
+                    color: @sdcui_color_blue;
+                }
+            }
+        }
+
+        .expand-collapse {
+            margin-top: 4px;
+            margin-bottom: 18px;
+            color: @sdcui_color_light-gray;
+        }
+
+        .interface-row {
+            width: 100%;
+            margin-top: 13px;
+            border-bottom: 1px solid @main_color_o;
+            padding-left: 4px;
+            min-height: 37px;
+
+
+            .interface-accordion {
+                cursor: pointer;
+
+                .chevron-container {
+                    position: relative;
+                    margin-right: 5px;
+
+                    &.isCollapsed {
+                        right: -6px;
+                        top: 0;
+                        * {
+                            transform: rotate(270deg);
+                        }
+                    }
+                    &:not(.isCollapsed) {
+                        top: 6px;
+                    }
+                    * {
+                        &:hover {
+                            cursor: pointer;
+                        }
+                    }
+                }
+                .interface-name {
+                    font-size: 18px;
+                    font-family: @font-opensans-bold;
+                    margin-bottom: 15px;
+                }
+            }
+
+            .generic-table {
+                margin-bottom: 24px;
+                margin-top: 10px;
+                margin-left: 22px;
+                font-size: 14px;
+
+                .header-row, .data-row {
+                    .cell {
+                        &.field-description {
+                            flex: 2.5;
+                        }
+
+                        &.field-actions {
+                            flex-basis: 72px;
+                            display: flex;
+                            justify-content: center;
+                            align-items: center;
+                        }
+                    }
+                }
+
+                .header-row {
+                    .cell {
+                        background: @sdcui_color_silver;
+
+                        &.field-actions {
+                            font-size: 10px;
+                        }
+                    }
+                }
+
+                .data-row {
+                    cursor: pointer;
+
+                    &:hover {
+                        background: @sdcui_color_light-silver;
+
+                        .cell {
+                            &.field-name {
+                                color: @sdcui_color_dark-blue;
+                            }
+                        }
+                    }
+
+                    &:not(:hover) {
+                       .field-actions {
+                            visibility: hidden;
+                        }
+                    }
+
+                    .cell {
+                        white-space: nowrap;
+                        text-overflow: ellipsis;
+                        overflow: hidden;
+
+                        &.field-description {
+                            &:not(.collapsed) {
+                                white-space: normal;
+                            }
+                            &.collapsed {
+                                text-overflow: clip;
+                            }
+                            .more-or-less {
+                                margin-left: 5px;
+                            }
+                        }
+
+                        &.field-actions {
+                            .delete-action {
+                                position: relative;
+                                top: 2px;
+                            }
+                        }
+                    }
+
+                }
+            }
+
+        }
+    }
+}
diff --git a/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.ts b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.ts
new file mode 100644 (file)
index 0000000..2a77b5e
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+*  Copyright (C) 2022 Nordix Foundation. All rights reserved.
+*  ================================================================================
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*  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 { SdcConfigToken, ISdcConfig } from "app/ng2/config/sdc-config.config";
+import {TranslateService } from "app/ng2/shared/translator/translate.service";
+
+import { ModalComponent } from 'app/ng2/components/ui/modal/modal.component';
+import {ModalService } from 'app/ng2/services/modal.service';
+import {
+    OperationModel,
+    InterfaceModel,
+    CapabilitiesGroup,
+    ButtonModel, ModalModel
+} from 'app/models';
+
+import {ComponentServiceNg2 } from 'app/ng2/services/component-services/component.service';
+
+import { SdcUiServices } from 'onap-ui-angular';
+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 {InterfaceOperationHandlerComponent} from "../composition/interface-operatons/operation-creator/interface-operation-handler.component";
+import {DropdownValue} from "../../components/ui/form-components/dropdown/ui-element-dropdown.component";
+
+export class UIOperationModel extends OperationModel {
+    isCollapsed: boolean = true;
+    isEllipsis: boolean;
+    MAX_LENGTH = 75;
+
+    constructor(operation: OperationModel) {
+        super(operation);
+
+        if (!operation.description) {
+            this.description = '';
+        }
+
+        if (this.description.length > this.MAX_LENGTH) {
+            this.isEllipsis = true;
+        } else {
+            this.isEllipsis = false;
+        }
+    }
+
+    getDescriptionEllipsis(): string {
+        if (this.isCollapsed && this.description.length > this.MAX_LENGTH) {
+            return this.description.substr(0, this.MAX_LENGTH - 3) + '...';
+        }
+        return this.description;
+    }
+
+    toggleCollapsed(e) {
+        e.stopPropagation();
+        this.isCollapsed = !this.isCollapsed;
+    }
+}
+
+// tslint:disable-next-line:max-classes-per-file
+class ModalTranslation {
+    CREATE_TITLE: string;
+    EDIT_TITLE: string;
+    DELETE_TITLE: string;
+    CANCEL_BUTTON: string;
+    SAVE_BUTTON: string;
+    CREATE_BUTTON: string;
+    DELETE_BUTTON: string;
+    deleteText: Function;
+
+    constructor(private TranslateService: TranslateService) {
+        this.TranslateService.languageChangedObservable.subscribe(lang => {
+            this.CREATE_TITLE = this.TranslateService.translate("INTERFACE_CREATE_TITLE");
+            this.EDIT_TITLE = this.TranslateService.translate('INTERFACE_EDIT_TITLE');
+            this.DELETE_TITLE = this.TranslateService.translate("INTERFACE_DELETE_TITLE");
+            this.CANCEL_BUTTON = this.TranslateService.translate("INTERFACE_CANCEL_BUTTON");
+            this.SAVE_BUTTON = this.TranslateService.translate("INTERFACE_SAVE_BUTTON");
+            this.CREATE_BUTTON = this.TranslateService.translate("INTERFACE_CREATE_BUTTON");
+            this.DELETE_BUTTON = this.TranslateService.translate("INTERFACE_DELETE_BUTTON");
+            this.deleteText = (operationName) => this.TranslateService.translate("INTERFACE_DELETE_TEXT", {operationName});
+        });
+    }
+}
+
+// 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)
+        );
+    }
+
+    toggleCollapse() {
+        this.isCollapsed = !this.isCollapsed;
+    }
+}
+
+// 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]
+})
+
+export class InterfaceDefinitionComponent {
+
+    modalInstance: ComponentRef<ModalComponent>;
+    interfaces: UIInterfaceModel[];
+    inputs: Array<InputOperationParameter> = [];
+
+    properties: Array<PropertyParamRowComponent> = [];
+    deploymentArtifactsFilePath: Array<DropdownValue> = [];
+
+    toscaArtifactTypes: Array<DropdownValue> = [];
+
+    isLoading: boolean;
+    interfaceTypes: { [interfaceType: string]: string[] };
+    modalTranslation: ModalTranslation;
+    workflows: any[];
+    capabilities: CapabilitiesGroup;
+
+    @Input() component: IComponent;
+    @Input() readonly: boolean;
+    @Input() enableMenuItems: Function;
+    @Input() disableMenuItems: Function;
+
+    constructor(
+        @Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
+        @Inject("$state") private $state: ng.ui.IStateService,
+        private translateService: TranslateService,
+        private componentServiceNg2: ComponentServiceNg2,
+        private modalServiceNg2: ModalService,
+        private modalServiceSdcUI: SdcUiServices.ModalService,
+        private topologyTemplateService: TopologyTemplateService
+    ) {
+        this.modalTranslation = new ModalTranslation(translateService);
+    }
+
+    ngOnInit(): void {
+        if(this.component) {
+            this.initInterfaceDefinition();
+        }
+    }
+
+    private cancelAndCloseModal = () => {
+        return this.modalServiceNg2.closeCurrentModal();
+    }
+
+    private enableOrDisableSaveButton = (): boolean => {
+        return true;
+    }
+
+    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 interfaceDataModal: ModalModel =
+            new ModalModel('l', this.modalTranslation.EDIT_TITLE, '', [saveButton, cancelButton], 'custom');
+        this.modalInstance = this.modalServiceNg2.createCustomModal(interfaceDataModal);
+
+        this.modalServiceNg2.addDynamicContentToModal(
+            this.modalInstance,
+            InterfaceOperationHandlerComponent,
+            {
+                deploymentArtifactsFilePath: this.deploymentArtifactsFilePath,
+                toscaArtifactTypes: this.toscaArtifactTypes,
+                selectedInterface: interfaceModel,
+                selectedInterfaceOperation: operation,
+                validityChangedCallback: this.enableOrDisableSaveButton,
+                isViewOnly: true
+            }
+        );
+        this.modalInstance.instance.open();
+    }
+
+    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.isLoading = false;
+        });
+    }
+
+    collapseAll(value: boolean = true): void {
+        _.forEach(this.interfaces, (interfaceData) => {
+            interfaceData.isCollapsed = value;
+        });
+    }
+
+    isAllCollapsed(): boolean {
+        return _.every(this.interfaces, (interfaceData) => interfaceData.isCollapsed);
+    }
+
+    isAllExpanded(): boolean {
+        return _.every(this.interfaces, (interfaceData) => !interfaceData.isCollapsed);
+    }
+
+    isInterfaceListEmpty(): boolean {
+        return this.interfaces.length === 0;
+    }
+
+    isOperationListEmpty(): boolean {
+        return _.filter(this.interfaces, (interfaceData) =>
+            interfaceData.operations && interfaceData.operations.length > 0).length > 0;
+    }
+
+}
index 20425f8..49f273c 100644 (file)
@@ -145,6 +145,10 @@ export class TopologyTemplateService {
         return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INFORMATIONAL_ARTIFACTS]);
     }
 
+    getComponentInterfaceOperations(componentType: string, componentId: string): Observable<ComponentGenericResponse> {
+        return this.getComponentDataByFieldsName(componentType, componentId, [COMPONENT_FIELDS.COMPONENT_INTERFACE_OPERATIONS]);
+    }
+
     getComponentInformationalArtifactsAndInstances(component: Component): Observable<ComponentGenericResponse> {
         return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INFORMATIONAL_ARTIFACTS, COMPONENT_FIELDS.COMPONENT_INSTANCES]);
     }
index 58aa402..ab706e7 100644 (file)
@@ -292,6 +292,7 @@ export class States {
   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_INTERFACE_DEFINITION = 'workspace.interface-definition';
   public static WORKSPACE_NETWORK_CALL_FLOW = 'workspace.network_call_flow';
   public static WORKSPACE_MANAGEMENT_WORKFLOW = 'workspace.management_workflow';
   public static WORKSPACE_DEPLOYMENT = 'workspace.deployment';
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view-model.ts
new file mode 100644 (file)
index 0000000..46ba031
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+*  Copyright (C) 2022 Nordix Foundation. All rights reserved.
+*  ================================================================================
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*  SPDX-License-Identifier: Apache-2.0
+*  ============LICENSE_END=========================================================
+*/
+'use strict';
+
+import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model";
+
+export interface IInterfaceDefinitionViewModelScope extends IWorkspaceViewModelScope {};
+
+export class InterfaceDefinitionViewModel {
+
+    static '$inject' = [
+        '$scope'
+    ];
+
+    constructor(private $scope: IInterfaceDefinitionViewModelScope) {}
+
+}
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view.html b/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view.html
new file mode 100644 (file)
index 0000000..4eba488
--- /dev/null
@@ -0,0 +1,27 @@
+<!--
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * Copyright (C) 2022 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+-->
+<div class="workspace-interface-definition">
+    <interface-definition
+        [component]="component"
+        [readonly]="isViewMode() || !isDesigner()"
+        [disableMenuItems]="disableMenuItems"
+        [enableMenuItems]="enableMenuItems">
+    </interface-definition>
+</div>
diff --git a/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition.less b/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition.less
new file mode 100644 (file)
index 0000000..ce0264e
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+* ============LICENSE_START=======================================================
+* SDC
+* ================================================================================
+*  Copyright (C) 2022 Nordix Foundation. All rights reserved.
+*  ================================================================================
+*  Licensed under the Apache License, Version 2.0 (the "License");
+*  you may not use this file except in compliance with the License.
+*  You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*  Unless required by applicable law or agreed to in writing, software
+*  distributed under the License is distributed on an "AS IS" BASIS,
+*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+*  See the License for the specific language governing permissions and
+*  limitations under the License.
+*
+*  SPDX-License-Identifier: Apache-2.0
+*  ============LICENSE_END=========================================================
+*/
+.workspace-interface-definition {
+    width: 100%;
+    display: inline-block;
+    top: -26px;
+    position: relative;
+}
index 1a79c0a..1779a0a 100644 (file)
     "SERVICE_PATH_SELECTOR_SHOW_ALL_VALUE" : "⚊ Show all âšŠ",
     "SERVICE_OPERATION_PROPERTY_TOOLTIP_TEXT": "Service properties are defined by the input parameter type. In case you can't find a certain parameter, it might be due to a wrong type selection.",
 
+    "=========== INTERFACE DEFINITION ==========": "",
+    "INTERFACE_DATA_EMPTY": "No Interface data to display",
+
     "=========== INTERFACE OPERATION ==========": "",
     "INTERFACE_ADD_OPERATION": "Add Operation",
     "INTERFACE_EXPAND_ALL": "Expand All",
index 834732b..bac6138 100644 (file)
@@ -47,6 +47,7 @@ import org.onap.sdc.frontend.ci.tests.flow.AddNodeToCompositionFlow;
 import org.onap.sdc.frontend.ci.tests.flow.CreateVfFlow;
 import org.onap.sdc.frontend.ci.tests.flow.CreateVfcFlow;
 import org.onap.sdc.frontend.ci.tests.flow.DownloadCsarArtifactFlow;
+import org.onap.sdc.frontend.ci.tests.flow.InterfaceDefinitionFlow;
 import org.onap.sdc.frontend.ci.tests.flow.exception.UiTestFlowRuntimeException;
 import org.onap.sdc.frontend.ci.tests.pages.AttributeModal;
 import org.onap.sdc.frontend.ci.tests.pages.AttributesPage;
@@ -55,9 +56,10 @@ import org.onap.sdc.frontend.ci.tests.pages.ResourceCreatePage;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionDetailSideBarComponent;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionDetailSideBarComponent.CompositionDetailTabName;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionInformationTab;
-import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionInterfaceOperationsModal;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionInterfaceOperationsTab;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionPage;
+import org.onap.sdc.frontend.ci.tests.pages.component.workspace.InterfaceDefinitionOperationsModal;
+import org.onap.sdc.frontend.ci.tests.pages.component.workspace.InterfaceDefinitionPage;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.ToscaArtifactsPage;
 import org.onap.sdc.frontend.ci.tests.pages.home.HomePage;
 import org.onap.sdc.frontend.ci.tests.utilities.FileHandling;
@@ -115,6 +117,10 @@ public class ImportVfcUiTest extends SetupCDTest {
         yamlObject = downloadToscaArtifact(componentPage);
         checkMetadata(yamlObject, vfcCreateData);
         checkNodeTypes(yamlObject);
+
+        componentPage = viewInterfaceDefinitionFromVFC(componentPage);
+        componentPage.isLoaded();
+
         homePage.getTopNavComponent().clickOnHome();
 
         // TC - Import VFC with interface inputs
@@ -135,6 +141,23 @@ public class ImportVfcUiTest extends SetupCDTest {
 
     }
 
+    private ComponentPage viewInterfaceDefinitionFromVFC(final ComponentPage componentPage) {
+        final InterfaceDefinitionFlow interfaceDefinitionFlow = new InterfaceDefinitionFlow(webDriver);
+        interfaceDefinitionFlow.run(componentPage);
+        final InterfaceDefinitionPage interfaceDefinitionPage = interfaceDefinitionFlow.getLandedPage()
+            .orElseThrow(() -> new UiTestFlowRuntimeException("Missing expected return InterfaceDefinitionPage"));
+        final var OPERATION_NAME = "create";
+        assertTrue(interfaceDefinitionPage.isInterfaceDefinitionOperationPresent(OPERATION_NAME));
+        final InterfaceDefinitionOperationsModal interfaceDefinitionOperationsModal = interfaceDefinitionPage.clickOnInterfaceDefinitionOperation(
+            OPERATION_NAME);
+        interfaceDefinitionOperationsModal.isLoaded();
+        ExtentTestActions
+            .takeScreenshot(Status.INFO, "clickOnOInterfaceOperation", "Interface Definition Operation Modal opened");
+        checkInterfaceDefinitionData(interfaceDefinitionOperationsModal);
+        interfaceDefinitionOperationsModal.clickOnCancel();
+        return interfaceDefinitionPage;
+    }
+
     @Test
     public void importVfcWithModel() {
         final String fileName = "VFC-For-Model.yaml";
@@ -146,6 +169,13 @@ public class ImportVfcUiTest extends SetupCDTest {
         componentPage.isLoaded();
     }
 
+    private void checkInterfaceDefinitionData(final InterfaceDefinitionOperationsModal interfaceDefinitionOperationsModal) {
+        assertTrue(interfaceDefinitionOperationsModal.getDescription().isEmpty());
+        assertEquals(interfaceDefinitionOperationsModal.getImplementationName(), "path/to/my/implementation.sh");
+        assertEquals(interfaceDefinitionOperationsModal.getInputName(), "first");
+        assertEquals(interfaceDefinitionOperationsModal.getInputValue(), "1234");
+    }
+
     private ComponentPage manageAttributes(final ComponentPage componentPage) {
         final AttributesPage attributesPage = componentPage.goToAttributes();
         attributesPage.isLoaded();
@@ -189,7 +219,7 @@ public class ImportVfcUiTest extends SetupCDTest {
         compositionInterfaceOperationsTab.isLoaded();
         ExtentTestActions.takeScreenshot(Status.INFO, "compositionInterfaceOperationsTab", "Composition Interface Operations Tab opened");
         assertTrue(compositionInterfaceOperationsTab.isOperationPresent("create"));
-        CompositionInterfaceOperationsModal compositionInterfaceOperationsModal = compositionInterfaceOperationsTab.clickOnOperation("create");
+        InterfaceDefinitionOperationsModal compositionInterfaceOperationsModal = compositionInterfaceOperationsTab.clickOnOperation("create");
         compositionInterfaceOperationsModal.isLoaded();
         ExtentTestActions
             .takeScreenshot(Status.INFO, "compositionInterfaceOperationsTab.clickOnOperation", "Composition Interface Operations Modal opened");
@@ -198,7 +228,7 @@ public class ImportVfcUiTest extends SetupCDTest {
         compositionInterfaceOperationsModal.addInput();
         ExtentTestActions.takeScreenshot(Status.INFO, "compositionInterfaceOperationsModal.addInput", "Adding Input");
 
-        final CompositionInterfaceOperationsModal.InterfaceOperationsData interfaceOperationsData = new CompositionInterfaceOperationsModal.InterfaceOperationsData
+        final InterfaceDefinitionOperationsModal.InterfaceOperationsData interfaceOperationsData = new InterfaceDefinitionOperationsModal.InterfaceOperationsData
             ("This is CREATE operation", "fullPath/to/my/newImplementation.sh", "second", "9876");
         compositionInterfaceOperationsModal.updateInterfaceOperation(interfaceOperationsData);
         compositionInterfaceOperationsTab.isLoaded();
@@ -370,8 +400,8 @@ public class ImportVfcUiTest extends SetupCDTest {
 
     }
 
-    private void checkCompositionInterfaceOperations(final CompositionInterfaceOperationsModal compositionInterfaceOperationsModal,
-                                                     final CompositionInterfaceOperationsModal.InterfaceOperationsData interfaceOperationsData) {
+    private void checkCompositionInterfaceOperations(final InterfaceDefinitionOperationsModal compositionInterfaceOperationsModal,
+                                                     final InterfaceDefinitionOperationsModal.InterfaceOperationsData interfaceOperationsData) {
         assertEquals(interfaceOperationsData.getDescription(), compositionInterfaceOperationsModal.getDescription());
         assertEquals(interfaceOperationsData.getImplementationName(), compositionInterfaceOperationsModal.getImplementationName());
         assertEquals(interfaceOperationsData.getInputName(), compositionInterfaceOperationsModal.getInputName());
index 8bec835..a2dbd57 100644 (file)
@@ -78,7 +78,7 @@ import org.onap.sdc.frontend.ci.tests.pages.ResourcePropertiesPage;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionDetailSideBarComponent;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionDetailSideBarComponent.CompositionDetailTabName;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionInformationTab;
-import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionInterfaceOperationsModal;
+import org.onap.sdc.frontend.ci.tests.pages.component.workspace.InterfaceDefinitionOperationsModal;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionInterfaceOperationsTab;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionPage;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.RelationshipWizardInterfaceOperation.InterfaceOperationsData;
@@ -247,8 +247,8 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
         compositionPage.isLoaded();
         ExtentTestActions.addScreenshot(Status.INFO, "select-VFC-node", "Selecting Node on composition");
         compositionPage.selectNode(vfcs.get(1).getName());
-        final CompositionInterfaceOperationsModal.InterfaceOperationsData interfaceOperationsData =
-            new CompositionInterfaceOperationsModal.InterfaceOperationsData("IT for updating an Interface Operation",
+        final InterfaceDefinitionOperationsModal.InterfaceOperationsData interfaceOperationsData =
+            new InterfaceDefinitionOperationsModal.InterfaceOperationsData("IT for updating an Interface Operation",
                 "MyIntegrationTestImplementationName", "My_IT_InputName", "My_IT_InputValue");
         updateInterfaceOperation(compositionPage, interfaceOperationsData);
         componentPage = compositionPage.goToGeneral();
@@ -374,7 +374,7 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
      * @throws IOException
      */
     private void updateInterfaceOperation(final CompositionPage compositionPage,
-        final CompositionInterfaceOperationsModal.InterfaceOperationsData interfaceOperationsData) throws IOException {
+        final InterfaceDefinitionOperationsModal.InterfaceOperationsData interfaceOperationsData) throws IOException {
         final CompositionDetailSideBarComponent detailSideBar = compositionPage.getDetailSideBar();
         detailSideBar.isLoaded();
         final CompositionInterfaceOperationsTab compositionInterfaceOperationsTab =
@@ -383,7 +383,7 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
         ExtentTestActions.takeScreenshot(Status.INFO, "compositionInterfaceOperationsTab",
             "Composition Interface Operations Tab loaded");
         assertTrue(compositionInterfaceOperationsTab.isOperationPresent(interfaceOperationName));
-        final CompositionInterfaceOperationsModal compositionInterfaceOperationsModal = compositionInterfaceOperationsTab
+        final InterfaceDefinitionOperationsModal compositionInterfaceOperationsModal = compositionInterfaceOperationsTab
             .clickOnOperation(interfaceOperationName);
         compositionInterfaceOperationsModal.isLoaded();
         ExtentTestActions.takeScreenshot(Status.INFO, "update-interface-operation-modal", "Loading Interface Operations Modal");
@@ -405,13 +405,13 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
      * @param interfaceOperationsData The Updated Interface Definition
      */
     private void validateUpdatedInterfaceOperation(final CompositionDetailSideBarComponent detailSideBar,
-                                                   final CompositionInterfaceOperationsModal.InterfaceOperationsData interfaceOperationsData) {
+                                                   final InterfaceDefinitionOperationsModal.InterfaceOperationsData interfaceOperationsData) {
         final CompositionInterfaceOperationsTab compositionInterfaceOperationsTab = (CompositionInterfaceOperationsTab) detailSideBar
             .selectTab(CompositionDetailTabName.INTERFACE_OPERATIONS);
         compositionInterfaceOperationsTab.isLoaded();
         assertTrue(compositionInterfaceOperationsTab.isOperationPresent(interfaceOperationName));
         assertTrue(compositionInterfaceOperationsTab.isDescriptionPresent());
-        final CompositionInterfaceOperationsModal compositionInterfaceOperationsModal = compositionInterfaceOperationsTab
+        final InterfaceDefinitionOperationsModal compositionInterfaceOperationsModal = compositionInterfaceOperationsTab
             .clickOnOperation(interfaceOperationName);
         compositionInterfaceOperationsModal.isLoaded();
         ExtentTestActions.takeScreenshot(Status.INFO, "validate-updated-interface-operation",
@@ -428,7 +428,7 @@ public class ServiceTemplateDesignUiTests extends SetupCDTest {
     }
 
     private void verifyToscaTemplateHasUpdatedInterfaceOperation(final Map<?, ?> toscaTemplateYaml,
-        final CompositionInterfaceOperationsModal.InterfaceOperationsData interfaceOperationsData) {
+        final InterfaceDefinitionOperationsModal.InterfaceOperationsData interfaceOperationsData) {
 
         assertNotNull(toscaTemplateYaml, "No contents in TOSCA Template");
         final Map<String, Object> topologyTemplateTosca = getMapEntry((Map<String, Object>) toscaTemplateYaml, "topology_template");
diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/flow/InterfaceDefinitionFlow.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/flow/InterfaceDefinitionFlow.java
new file mode 100644 (file)
index 0000000..a0955ff
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.sdc.frontend.ci.tests.flow;
+
+import com.aventstack.extentreports.Status;
+import java.util.Optional;
+import org.onap.sdc.frontend.ci.tests.execute.setup.ExtentTestActions;
+import org.onap.sdc.frontend.ci.tests.pages.ComponentPage;
+import org.onap.sdc.frontend.ci.tests.pages.PageObject;
+import org.onap.sdc.frontend.ci.tests.pages.component.workspace.InterfaceDefinitionPage;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * UI Flow for managing an Interface Definition from a component
+ */
+public class InterfaceDefinitionFlow extends AbstractUiTestFlow {
+
+    private InterfaceDefinitionPage interfaceDefinitionPage;
+
+    public InterfaceDefinitionFlow(final WebDriver webDriver) {
+        super(webDriver);
+    }
+
+    @Override
+    public Optional<PageObject> run(final PageObject... pageObjects) {
+        extendTest.log(Status.INFO, "Downloading Tosca CSAR generated");
+        final ComponentPage componentPage = findParameter(pageObjects, ComponentPage.class);
+        componentPage.isLoaded();
+        interfaceDefinitionPage = componentPage.goToInterfaceDefinition();
+        interfaceDefinitionPage.isLoaded();
+        ExtentTestActions.takeScreenshot(Status.INFO, "interface-definition-page", "Interface Definition page loaded");
+        return Optional.of(interfaceDefinitionPage);
+    }
+
+    @Override
+    public Optional<InterfaceDefinitionPage> getLandedPage() {
+        return Optional.ofNullable(interfaceDefinitionPage);
+    }
+}
index ada3410..f690cca 100644 (file)
@@ -20,6 +20,7 @@ package org.onap.sdc.frontend.ci.tests.pages;
 
 import org.onap.sdc.frontend.ci.tests.datatypes.LifeCycleStateEnum;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionPage;
+import org.onap.sdc.frontend.ci.tests.pages.component.workspace.InterfaceDefinitionPage;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.ToscaArtifactsPage;
 import org.onap.sdc.frontend.ci.tests.pages.home.HomePage;
 import org.onap.sdc.frontend.ci.tests.utilities.LoaderHelper;
@@ -59,6 +60,10 @@ public class ComponentPage extends AbstractPageObject {
         return resourceLeftSideMenu.clickOnToscaArtifactsMenuItem();
     }
 
+    public InterfaceDefinitionPage goToInterfaceDefinition() {
+        return resourceLeftSideMenu.clickOnInterfaceDefinitionMenuItem();
+    }
+
     public CompositionPage goToComposition() {
         return resourceLeftSideMenu.clickOnCompositionMenuItem();
     }
index f0a9739..68a447e 100644 (file)
@@ -22,6 +22,7 @@ package org.onap.sdc.frontend.ci.tests.pages;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionPage;
+import org.onap.sdc.frontend.ci.tests.pages.component.workspace.InterfaceDefinitionPage;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.ToscaArtifactsPage;
 import org.openqa.selenium.By;
 import org.openqa.selenium.WebDriver;
@@ -94,6 +95,16 @@ public class ResourceLeftSideMenu extends AbstractPageObject {
         return new ToscaArtifactsPage(webDriver);
     }
 
+    /**
+     * Clicks on the Interface Definition menu item.
+     *
+     * @return the next page object
+     */
+    public InterfaceDefinitionPage clickOnInterfaceDefinitionMenuItem() {
+        wrappingElement.findElement(By.xpath(XpathSelector.INTERFACE_DEFINITION_MENU.getXpath())).click();
+        return new InterfaceDefinitionPage(webDriver);
+    }
+
     /**
      * Clicks on the 'General' menu item.
      *
@@ -127,7 +138,8 @@ public class ResourceLeftSideMenu extends AbstractPageObject {
         GENERAL_MENU("GeneralLeftSideMenu", "//*[@data-tests-id='%s']"),
         COMPOSITION_MENU("CompositionLeftSideMenu", "//*[@data-tests-id='%s']"),
         REQUIREMENT_CAPABILITY_MENU("Req. & CapabilitiesLeftSideMenu", "//*[@data-tests-id='%s']"),
-        TOSCA_ARTIFACTS_MENU("TOSCA ArtifactsLeftSideMenu", "//*[@data-tests-id='%s']");
+        TOSCA_ARTIFACTS_MENU("TOSCA ArtifactsLeftSideMenu", "//*[@data-tests-id='%s']"),
+        INTERFACE_DEFINITION_MENU("InterfacesLeftSideMenu", "//*[@data-tests-id='%s']");
 
         @Getter
         private final String id;
index c672939..60a732a 100644 (file)
@@ -19,8 +19,6 @@
 
 package org.onap.sdc.frontend.ci.tests.pages.component.workspace;
 
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import org.onap.sdc.frontend.ci.tests.pages.AbstractPageObject;
@@ -66,10 +64,10 @@ public class CompositionInterfaceOperationsTab extends AbstractPageObject {
         }
     }
 
-    public CompositionInterfaceOperationsModal clickOnOperation(final String operationName) {
+    public InterfaceDefinitionOperationsModal clickOnOperation(final String operationName) {
         final WebElement webElementInterfaceRow = webElement.findElement(By.xpath(XpathSelector.INTERFACE_ROW.getXPath()));
         webElementInterfaceRow.findElement(By.xpath(XpathSelector.FIELD_NAME_SPAN.getXPath(operationName))).click();
-        return new CompositionInterfaceOperationsModal(webDriver);
+        return new InterfaceDefinitionOperationsModal(webDriver);
     }
 
     @AllArgsConstructor
@@ -31,9 +31,9 @@ import org.openqa.selenium.WebElement;
 /**
  * Represents the Composition Interface Operations Modal.
  */
-public class CompositionInterfaceOperationsModal extends AbstractPageObject {
+public class InterfaceDefinitionOperationsModal extends AbstractPageObject {
 
-    public CompositionInterfaceOperationsModal(final WebDriver webDriver) {
+    public InterfaceDefinitionOperationsModal(final WebDriver webDriver) {
         super(webDriver);
     }
 
@@ -44,9 +44,8 @@ public class CompositionInterfaceOperationsModal extends AbstractPageObject {
         waitForElementVisibility(By.xpath(XpathSelector.OPERATION_NAME_LABEL.getXPath()));
         waitForElementVisibility(By.xpath(XpathSelector.INPUT_NAME_SPAN.getXPath()));
         waitForElementVisibility(By.xpath(XpathSelector.INPUT_VALUE_SPAN.getXPath()));
-
-        waitToBeClickable(By.xpath(XpathSelector.ADD_INPUT_BTN.getXPath()));
-        waitToBeClickable(By.xpath(XpathSelector.SAVE_BTN.getXPath()));
+        waitForElementVisibility(By.xpath(XpathSelector.ADD_INPUT_BTN.getXPath()));
+        waitForElementVisibility(By.xpath(XpathSelector.SAVE_BTN.getXPath()));
         waitToBeClickable(By.xpath(XpathSelector.CANCEL_BTN.getXPath()));
     }
 
diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceDefinitionPage.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceDefinitionPage.java
new file mode 100644 (file)
index 0000000..a40878a
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.sdc.frontend.ci.tests.pages.component.workspace;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.onap.sdc.frontend.ci.tests.pages.ComponentPage;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+public class InterfaceDefinitionPage extends ComponentPage {
+
+    private WebElement wrappingElement;
+
+    public InterfaceDefinitionPage(final WebDriver webDriver) {
+        super(webDriver);
+    }
+
+    @Override
+    public void isLoaded() {
+        wrappingElement = waitForElementVisibility(By.xpath(XpathSelector.MAIN_DIV.getXpath()), 5);
+        waitForElementVisibility(By.xpath(XpathSelector.TITLE_DIV.getXpath()), 5);
+        waitForElementVisibility(By.xpath(XpathSelector.INTERFACE_NAME_SPAN.getXpath()), 5);
+    }
+
+    public boolean isInterfaceDefinitionOperationPresent(final String operationName) {
+        try {
+            final WebElement webElementInterfaceRow = wrappingElement.findElement(
+                By.xpath(InterfaceDefinitionPage.XpathSelector.INTERFACE_ROW.getXpath()));
+            webElementInterfaceRow.findElement(By.xpath(InterfaceDefinitionPage.XpathSelector.FIELD_NAME_SPAN.getXpath(operationName)));
+        } catch (final Exception e) {
+            return false;
+        }
+        return true;
+    }
+
+    public InterfaceDefinitionOperationsModal clickOnInterfaceDefinitionOperation(final String operationName) {
+        final WebElement webElementInterfaceRow = wrappingElement.findElement(
+            By.xpath(InterfaceDefinitionPage.XpathSelector.INTERFACE_ROW.getXpath()));
+        webElementInterfaceRow.findElement(By.xpath(InterfaceDefinitionPage.XpathSelector.FIELD_NAME_SPAN.getXpath(operationName))).click();
+        return new InterfaceDefinitionOperationsModal(webDriver);
+    }
+
+    /**
+     * Enum that contains identifiers and xpath expressions to elements related to the enclosing page object.
+     */
+    @AllArgsConstructor
+    private enum XpathSelector {
+        MAIN_DIV("w-sdc-main-right-container", "//div[@class='%s']"),
+        TITLE_DIV("tab-title", "//div[contains(@class,'%s') and contains(text(), 'Interfaces')]"),
+        INTERFACE_NAME_SPAN("//span[@class='interface-name']"),
+
+        INTERFACE_OPERATIONS("//div[@class='interface-operations']"),
+        OPERATION_LIST("//div[@class='operation-list']"),
+        EXPAND_COLLAPSE("//div[@class='expand-collapse']"),
+        INTERFACE_ACCORDION("//div[@class='interface-accordion']"),
+        INTERFACE_ROW("//div[contains(@class,'interface-row')]"),
+        FIELD_NAME_SPAN("//span[contains(@class,'field-name') and contains(text(), '%s')]"),
+        FIELD_DESCRIPTION_SPAN("//span[contains(@class,'field-description')]");
+
+        @Getter
+        private String id;
+        private final String xpathFormat;
+
+        XpathSelector(final String xpathFormat) {
+            this.xpathFormat = xpathFormat;
+        }
+
+        public String getXpath() {
+            return String.format(xpathFormat, id);
+        }
+
+        public String getXpath(final String... params) {
+            return String.format(xpathFormat, params);
+        }
+
+    }
+}