diff options
26 files changed, 960 insertions, 34 deletions
diff --git a/catalog-ui/configurations/menu.js b/catalog-ui/configurations/menu.js index beecfd16b2..38f504bb57 100644 --- a/catalog-ui/configurations/menu.js +++ b/catalog-ui/configurations/menu.js @@ -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"} ], diff --git a/catalog-ui/src/app/app.ts b/catalog-ui/src/app/app.ts index 87930fd12e..ffa43895a7 100644 --- a/catalog-ui/src/app/app.ts +++ b/catalog-ui/src/app/app.ts @@ -512,6 +512,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', parent: 'workspace', diff --git a/catalog-ui/src/app/modules/directive-module.ts b/catalog-ui/src/app/modules/directive-module.ts index 270a764e86..7719f7389f 100644 --- a/catalog-ui/src/app/modules/directive-module.ts +++ b/catalog-ui/src/app/modules/directive-module.ts @@ -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: [], diff --git a/catalog-ui/src/app/modules/view-model-module.ts b/catalog-ui/src/app/modules/view-model-module.ts index 5b8fc5944a..dd08135f33 100644 --- a/catalog-ui/src/app/modules/view-model-module.ts +++ b/catalog-ui/src/app/modules/view-model-module.ts @@ -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); diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts index 5b12ae9f48..f6ba919427 100644 --- a/catalog-ui/src/app/ng2/app.module.ts +++ b/catalog-ui/src/app/ng2/app.module.ts @@ -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, diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts index 2cc91a92a0..60d66788ac 100644 --- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts +++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/interface-operations.component.ts @@ -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(); diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html index 428c4cd5ed..6dec4160f6 100644 --- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html +++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.html @@ -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> @@ -122,7 +128,7 @@ <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> diff --git a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts index 6e4ae45487..1099391548 100644 --- a/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts +++ b/catalog-ui/src/app/ng2/pages/composition/interface-operatons/operation-creator/interface-operation-handler.component.ts @@ -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 index 0000000000..27a7f111d2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.module.ts @@ -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 index 0000000000..25ccf111a1 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.html @@ -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 index 0000000000..2b76c8c0ec --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.less @@ -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 index 0000000000..2a77b5e996 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/interface-definition/interface-definition.page.component.ts @@ -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; + } + +} diff --git a/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts b/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts index 20425f810a..49f273c716 100644 --- a/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/topology-template.service.ts @@ -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]); } diff --git a/catalog-ui/src/app/utils/constants.ts b/catalog-ui/src/app/utils/constants.ts index 58aa402b02..ab706e7088 100644 --- a/catalog-ui/src/app/utils/constants.ts +++ b/catalog-ui/src/app/utils/constants.ts @@ -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 index 0000000000..46ba0317d9 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view-model.ts @@ -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 index 0000000000..4eba48871d --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition-view.html @@ -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 index 0000000000..ce0264e13d --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/interface-definition/interface-definition.less @@ -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; +} diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index 1a79c0a6bb..1779a0a92d 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -441,6 +441,9 @@ "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", diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java index 834732b6f2..bac613861b 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java @@ -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()); diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ServiceTemplateDesignUiTests.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ServiceTemplateDesignUiTests.java index 8bec8353ef..a2dbd5729e 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ServiceTemplateDesignUiTests.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ServiceTemplateDesignUiTests.java @@ -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 index 0000000000..a0955ff800 --- /dev/null +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/flow/InterfaceDefinitionFlow.java @@ -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); + } +} diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java index ada3410318..f690cca3f5 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java @@ -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(); } diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java index f0a973910f..68a447eadf 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java @@ -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; @@ -95,6 +96,16 @@ public class ResourceLeftSideMenu extends AbstractPageObject { } /** + * 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. * * @return the next page object @@ -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; diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsTab.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsTab.java index c67293939e..60a732afd5 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsTab.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsTab.java @@ -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 diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsModal.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceDefinitionOperationsModal.java index 8fac69c944..cb2ee3d282 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionInterfaceOperationsModal.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceDefinitionOperationsModal.java @@ -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 index 0000000000..a40878ac14 --- /dev/null +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/InterfaceDefinitionPage.java @@ -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); + } + + } +} |