Interface operation screen enhancements
Change-Id: I2b510a4bf27ddf5730ed044cf77aebd955ad5862
Issue-ID: SDC-2044
Signed-off-by: Arielk <Ariel.Kenan@amdocs.com>
diff --git a/catalog-ui/src/app/models/operation.ts b/catalog-ui/src/app/models/operation.ts
index 2aa1332..6eeccec 100644
--- a/catalog-ui/src/app/models/operation.ts
+++ b/catalog-ui/src/app/models/operation.ts
@@ -3,15 +3,15 @@
export class OperationParameter {
name: string;
type: String;
- property: string;
- mandatory: boolean;
+ inputId: string;
+ required: boolean;
constructor(param?: OperationParameter) {
if (param) {
this.name = param.name;
this.type = param.type;
- this.property = param.property;
- this.mandatory = param.mandatory;
+ this.inputId = param.inputId;
+ this.required = param.required;
}
}
}
@@ -26,13 +26,13 @@
static EXISTING = 'EXISTING';
}
-export class OperationModel {
- operationType: string;
+export class BEOperationModel {
+ name: string;
description: string;
uniqueId: string;
- inputParams: IOperationParamsList;
- outputParams: IOperationParamsList;
+ inputs: IOperationParamsList;
+ outputs: IOperationParamsList;
workflowAssociationType: string;
workflowId: string;
@@ -40,12 +40,12 @@
constructor(operation?: any) {
if (operation) {
- this.operationType = operation.operationType;
+ this.name = operation.name;
this.description = operation.description;
this.uniqueId = operation.uniqueId;
- this.inputParams = operation.inputParams;
- this.outputParams = operation.outputParams;
+ this.inputs = operation.inputs;
+ this.outputs = operation.outputs;
this.workflowAssociationType = operation.workflowAssociationType;
this.workflowId = operation.workflowId;
@@ -53,23 +53,66 @@
}
}
- public createInputParamsList(inputParams: Array<OperationParameter>): void {
- this.inputParams = {
- listToscaDataDefinition: inputParams
+ public createInputsList(inputs: Array<OperationParameter>): void {
+ this.inputs = {
+ listToscaDataDefinition: inputs
};
}
- public createOutputParamsList(outputParams: Array<OperationParameter>): void {
- this.outputParams = {
- listToscaDataDefinition: _.map(outputParams, output => {
- const newOutput = {...output};
- delete newOutput.property;
- return newOutput;
+ public createOutputsList(outputs: Array<OperationParameter>): void {
+ this.outputs = {
+ listToscaDataDefinition: _.map(outputs, output => {
+ delete output.inputId;
+ return output;
})
};
}
}
-export interface CreateOperationResponse extends OperationModel {
+export class OperationModel extends BEOperationModel {
+ interfaceType: string;
+ interfaceId: string;
+
+ constructor(operation?: any) {
+ super(operation);
+ if (operation) {
+ this.interfaceId = operation.interfaceId;
+ this.interfaceType = operation.interfaceType;
+ }
+ }
+
+ public displayName(): string {
+ const lastDot = this.name ? this.name.lastIndexOf('.') : -1;
+ return lastDot === -1 ? this.name : this.name.substr(lastDot + 1);
+ }
+}
+
+export class CreateOperationResponse extends OperationModel {
artifactUUID: string;
+
+ constructor(operation?: any) {
+ super(operation);
+ if (operation) {
+ this.artifactUUID = operation.artifactUUID;
+ }
+ }
+}
+
+export class InterfaceModel {
+ type: string;
+ uniqueId: string;
+ operations: Array<OperationModel>;
+
+ constructor(interf?: any) {
+ if (interf) {
+ this.type = interf.type;
+ this.uniqueId = interf.uniqueId;
+ this.operations = interf.operations;
+ }
+ }
+
+ public displayType(): string {
+ const lastDot = this.type ? this.type.lastIndexOf('.') : -1;
+ return lastDot === -1 ? this.type : this.type.substr(lastDot + 1);
+ }
}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts
index 3414769..6292d85 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.module.ts
@@ -1,7 +1,9 @@
import {NgModule} from "@angular/core";
import {CommonModule} from "@angular/common";
import {InterfaceOperationComponent} from "./interface-operation.page.component";
+import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index";
import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
+import {TranslateModule} from "app/ng2/shared/translator/translate.module";
@NgModule({
declarations: [
@@ -9,7 +11,9 @@
],
imports: [
CommonModule,
- UiElementsModule
+ SdcUiComponentsModule,
+ UiElementsModule,
+ TranslateModule
],
exports: [],
entryComponents: [
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html
index c9fdf94..afeff48 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.html
@@ -1,56 +1,142 @@
<!--
- ~ Copyright © 2016-2018 European Support Limited
- ~
- ~ 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.
- -->
-
+ ~ Copyright © 2016-2018 European Support Limited
+ ~
+ ~ 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.
+ -->
+
<div class="interface-operation">
<loader [display]="isLoading" [size]="'large'" [relative]="true"></loader>
<div
- class="add-btn"
+ class="top-add-btn add-btn"
+ *ngIf="!isListEmpty()"
[ngClass]="{'disabled': readonly}"
data-tests-id="addInterfaceOperation"
(click)="onEditOperation()">
- Add Operation
+ {{ 'INTERFACE_ADD_OPERATION' | translate }}
</div>
<div class="operation-list">
<div
- class="operation-row"
- *ngFor="let operation of operationList"
- (click)="onEditOperation(operation)">
-
- <span class="operation-info-container">
- <span class="operation-title">
- <p class="operation-text" data-tests-id="interfaceOperationType">{{operation.operationType}}</p>
- </span>
-
- <span class="operation-description">
- <p class="operation-text" data-tests-id="interfaceOperationDescription">{{operation.description}}</p>
- </span>
- </span>
-
- <span class="operation-dumbo" *ngIf="!readonly">
- <span
- class="sprite-new delete-item-icon"
- data-tests-id="deleteInterfaceOperation"
- (click)="onRemoveOperation($event, operation)">
- </span>
- </span>
-
+ class="empty-list-container"
+ *ngIf="isListEmpty() && !isLoading">
+ <div
+ class="empty-list-add-btn"
+ [ngClass]="{'disabled': readonly}"
+ (click)="onEditOperation()">
+ <svg-icon
+ name="plus-circle"
+ mode="primary"
+ size="x_large"
+ [disabled]="readonly">
+ </svg-icon>
+ <div class="button-text">{{ 'INTERFACE_ADD_OPERATION' | translate }}</div>
+ </div>
</div>
+
+ <div *ngIf="!isListEmpty()">
+
+ <div class="expand-collapse">
+ <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}">
+ <svg-icon
+ name="caret1-down-o"
+ mode="primary"
+ size="small">
+ </svg-icon>
+ </span>
+ <span class="interface-name">{{interface.displayType()}}</span>
+ </div>
+
+ <div
+ class="generic-table"
+ *ngIf="!interface.isCollapsed">
+ <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>
+ <span
+ class="cell header-cell field-actions header-actions">
+ ●●●
+ </span>
+ </div>
+
+ <div
+ class="data-row"
+ *ngFor="let operation of interface.operations"
+ (click)="onEditOperation(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>
+ <span class="cell field-actions">
+ <span
+ class="delete-action"
+ data-tests-id="deleteOperation"
+ (click)="onRemoveOperation($event, operation)">
+ <svg-icon
+ *ngIf="!readonly"
+ name="trash-o"
+ mode="info"
+ size="small"
+ [clickable]="true">
+ </svg-icon>
+ </span>
+ </span>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
</div>
</div>
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less
index 4355020..b79e2f5 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.less
@@ -1,88 +1,196 @@
@import '../../../../assets/styles/variables.less';
+@import '../../../../assets/styles/override.less';
.interface-operation {
- .add-btn {
+ font-size: 14px;
+
+ .top-add-btn {
position: relative;
top: -31px;
text-transform: uppercase;
font-size: 14px;
+ font-family: @font-opensans-medium;
}
- a:not(.disabled) {
- &:hover {
- cursor: pointer;
+ .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: 25px;
+ padding-top: 5px;
- .operation-row {
+ .empty-list-container {
width: 100%;
- border: 1px solid @main_color_o;
display: flex;
- justify-content: space-between;
- align-items: center;
- height: 100px;
+ justify-content: center;
- &:hover {
- border-color: @main_color_c;
- cursor: pointer;
- }
-
- &:not(:first-child) {
- margin-top: 20px;
- }
-
- .operation-info-container {
- height: 100%;
- display: flex;
- align-items: center;
- margin-right: 60px;
-
- .operation-title, .operation-description {
- display: flex;
- align-items: center;
-
- .operation-text {
- overflow: hidden;
- margin-bottom: 0;
- max-height: 5rem;
- }
- }
-
- .operation-title {
- flex-shrink: 0;
- width: 200px;
- height: calc(100% - 13px);
- border-right: 1px solid @main_color_o;
- margin: 4px 0;
- padding: 0 30px;
-
- .operation-text {
- white-space: nowrap;
- text-overflow: ellipsis;
- }
- }
-
- .operation-description {
- padding-left: 30px;
- text-align: left;
- font-family: @font-opensans-regular;
-
- .operation-text {
- word-break: break-word;
- }
- }
- }
-
- .operation-dumbo {
- padding-right: 20px;
+ .empty-list-add-btn {
display: flex;
flex-direction: column;
- align-items: left;
+ 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-operation/interface-operation.page.component.ts b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.ts
index e19d345..e9b2001 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.ts
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/interface-operation.page.component.ts
@@ -3,12 +3,17 @@
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 {Observable} from "rxjs/Observable";
import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component';
import {ModalService} from 'app/ng2/services/modal.service';
-import {ModalModel, ButtonModel, InputBEModel, OperationModel, CreateOperationResponse, WORKFLOW_ASSOCIATION_OPTIONS} from 'app/models';
+import {ModalModel, ButtonModel, InputBEModel, OperationModel, InterfaceModel, CreateOperationResponse, WORKFLOW_ASSOCIATION_OPTIONS} from 'app/models';
+
+import {IModalConfig, IModalButtonComponent} from "sdc-ui/lib/angular/modals/models/modal-config";
+import {SdcUiComponents} from "sdc-ui/lib/angular";
+import {ModalButtonComponent} from "sdc-ui/lib/angular/components";
import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service';
import {ComponentGenericResponse} from 'app/ng2/services/responses/component-generic-response';
@@ -16,21 +21,96 @@
import {OperationCreatorComponent, OperationCreatorInput} from './operation-creator/operation-creator.component';
+export class UIOperationModel extends OperationModel {
+ isCollapsed: boolean = true;
+ isEllipsis: boolean;
+ MAX_LENGTH = 75;
+ _description: string;
+
+ 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;
+ }
+}
+
+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});
+ });
+ }
+}
+
+export class UIInterfaceModel extends InterfaceModel {
+ isCollapsed: boolean = false;
+
+ constructor(interf?: any) {
+ super(interf);
+ this.operations = _.map(
+ this.operations,
+ operation => new UIOperationModel(operation)
+ );
+ }
+
+ toggleCollapse() {
+ this.isCollapsed = !this.isCollapsed;
+ }
+}
+
@Component({
selector: 'interface-operation',
templateUrl: './interface-operation.page.component.html',
styleUrls: ['interface-operation.page.component.less'],
- providers: [ModalService]
+ providers: [ModalService, TranslateService]
})
export class InterfaceOperationComponent {
+ interfaces: Array<UIInterfaceModel>;
modalInstance: ComponentRef<ModalComponent>;
- operationList: Array<OperationModel> = [];
openOperation: OperationModel;
enableWorkflowAssociation: boolean;
inputs: Array<InputBEModel>;
isLoading: boolean;
+ interfaceTypes:{ [interfaceType: string]: Array<string> };
+ modalTranslation: ModalTranslation;
@Input() component: IComponent;
@Input() readonly: boolean;
@@ -40,40 +120,79 @@
constructor(
@Inject(SdcConfigToken) private sdcConfig: ISdcConfig,
@Inject("$state") private $state: ng.ui.IStateService,
+ private TranslateService: TranslateService,
private ComponentServiceNg2: ComponentServiceNg2,
private WorkflowServiceNg2: WorkflowServiceNg2,
private ModalServiceNg2: ModalService,
+ private ModalServiceSdcUI: SdcUiComponents.ModalService
) {
this.enableWorkflowAssociation = sdcConfig.enableWorkflowAssociation;
+ this.modalTranslation = new ModalTranslation(TranslateService);
}
ngOnInit(): void {
this.isLoading = true;
Observable.forkJoin(
- this.ComponentServiceNg2.getInterfaceOperations(this.component),
- this.ComponentServiceNg2.getComponentInputs(this.component)
- ).subscribe((response) => {
+ this.ComponentServiceNg2.getInterfaces(this.component),
+ this.ComponentServiceNg2.getComponentInputs(this.component),
+ this.ComponentServiceNg2.getInterfaceTypes(this.component)
+ ).subscribe((response: Array<any>) => {
this.isLoading = false;
- this.component.interfaceOperations = response[0].interfaceOperations;
- this.operationList = _.toArray(response[0].interfaceOperations).sort((a, b) => a.operationType.localeCompare(b.operationType));
+ this.initInterfaces(response[0].interfaces);
+ this.sortInterfaces();
this.inputs = response[1].inputs;
+ this.interfaceTypes = response[2];
});
}
+ initInterfaces(interfaces: Array<InterfaceModel>): void {
+ this.interfaces = _.map(interfaces, interf => new UIInterfaceModel(interf));
+ }
+
+ sortInterfaces(): void {
+ this.interfaces = _.filter(this.interfaces, interf => interf.operations && interf.operations.length > 0); // remove empty interfaces
+ this.interfaces.sort((a, b) => a.type.localeCompare(b.type)); // sort interfaces alphabetically
+ _.forEach(this.interfaces, interf => {
+ interf.operations.sort((a, b) => a.name.localeCompare(b.name)); // sort operations alphabetically
+ });
+ }
+
+ collapseAll(value: boolean = true): void {
+ _.forEach(this.interfaces, interf => {
+ interf.isCollapsed = value;
+ });
+ }
+
+ isAllCollapsed(): boolean {
+ return _.every(this.interfaces, interf => interf.isCollapsed);
+ }
+
+ isAllExpanded(): boolean {
+ return _.every(this.interfaces, interf => !interf.isCollapsed);
+ }
+
+ isListEmpty(): boolean {
+ return _.filter(
+ this.interfaces,
+ interf => interf.operations && interf.operations.length > 0
+ ).length === 0;
+ }
+
getDisabled = (): boolean => {
return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit();
}
onEditOperation = (operation?: OperationModel): void => {
+
const modalMap = {
create: {
- modalTitle: 'Create a New Operation',
- saveBtnText: 'Create',
+ modalTitle: this.modalTranslation.CREATE_TITLE,
+ saveBtnText: this.modalTranslation.CREATE_BUTTON,
submitCallback: this.createOperation,
},
edit: {
- modalTitle: 'Edit Operation',
- saveBtnText: 'Save',
+ modalTitle: this.modalTranslation.EDIT_TITLE,
+ saveBtnText: this.modalTranslation.SAVE_BUTTON,
submitCallback: this.updateOperation,
}
};
@@ -86,23 +205,28 @@
}
}
- const cancelButton: ButtonModel = new ButtonModel(
- 'Cancel',
- 'outline white',
- () => {
+ const cancelButton: IModalButtonComponent = {
+ id: 'cancelButton',
+ text: this.modalTranslation.CANCEL_BUTTON,
+ type: 'secondary',
+ size: 'small',
+ closeModal: true,
+ callback: () => {
this.openOperation = null;
- this.ModalServiceNg2.closeCurrentModal();
},
- );
+ };
- const saveButton: ButtonModel = new ButtonModel(
- modalData.saveBtnText,
- 'blue',
- () => {
- this.modalInstance.instance.dynamicContent.instance.createParamLists();
- this.ModalServiceNg2.closeCurrentModal();
+ const saveButton: IModalButtonComponent = {
+ id: 'saveButton',
+ text: modalData.saveBtnText,
+ type: 'primary',
+ size: 'small',
+ closeModal: true,
+ callback: () => {
+ const modalInstance = this.ModalServiceSdcUI.getCurrentInstance().innerModalContent.instance;
- const {operation, isUsingExistingWF} = this.modalInstance.instance.dynamicContent.instance;
+ const {operation, isUsingExistingWF, createParamLists} = modalInstance;
+ createParamLists();
this.openOperation = {...operation};
if (this.enableWorkflowAssociation && !isUsingExistingWF()) {
@@ -111,74 +235,81 @@
}
modalData.submitCallback(operation);
- },
- this.getDisabled,
- );
+ }
+ };
- const modalModel: ModalModel = new ModalModel(
- 'l',
- modalData.modalTitle,
- '',
- [saveButton, cancelButton],
- 'standard',
- );
-
- this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel);
-
- let input: OperationCreatorInput = {
- operation,
+ const input: OperationCreatorInput = {
+ inputOperation: operation,
inputProperties: this.inputs,
enableWorkflowAssociation: this.enableWorkflowAssociation,
readonly: this.readonly,
- isService: this.component.isService()
- }
+ isService: this.component.isService(),
+ interfaceTypes: this.interfaceTypes,
+ validityChangedCallback: this.enableOrDisableSaveButton
+ };
- this.ModalServiceNg2.addDynamicContentToModal(
- this.modalInstance,
- OperationCreatorComponent,
- input,
- );
+ const modalConfig: IModalConfig = {
+ title: modalData.modalTitle,
+ size: 'l',
+ type: 'custom',
+ buttons: [saveButton, cancelButton] as IModalButtonComponent[]
+ };
- this.modalInstance.instance.open();
+ this.ModalServiceSdcUI.openCustomModal(modalConfig, OperationCreatorComponent, input);
+
+ }
+
+ private enableOrDisableSaveButton = (shouldEnable: boolean): void => {
+ let saveButton: ModalButtonComponent = this.ModalServiceSdcUI.getCurrentInstance().getButtonById('saveButton');
+ saveButton.disabled = !shouldEnable;
}
onRemoveOperation = (event: Event, operation: OperationModel): void => {
event.stopPropagation();
const confirmCallback = () => {
- this.ModalServiceNg2.closeCurrentModal();
this.ComponentServiceNg2
.deleteInterfaceOperation(this.component, operation)
.subscribe(() => {
- const index = _.findIndex(this.operationList, el => el.uniqueId === operation.uniqueId);
- this.operationList.splice(index, 1);
- this.component.interfaceOperations = this.operationList;
+ const curInterf = _.find(this.interfaces, interf => interf.type === operation.interfaceType);
+ const index = _.findIndex(curInterf.operations, el => el.uniqueId === operation.uniqueId);
+ curInterf.operations.splice(index, 1);
+ if (!curInterf.operations.length) {
+ const interfIndex = _.findIndex(this.interfaces, interf => interf.type === operation.interfaceType);
+ this.interfaces.splice(interfIndex, 1);
+ }
});
}
- this.modalInstance = this.ModalServiceNg2.createActionModal(
- operation.operationType,
- 'Are you sure you want to delete this operation?',
- 'Delete',
+ this.ModalServiceSdcUI.openAlertModal(
+ this.modalTranslation.DELETE_TITLE,
+ this.modalTranslation.deleteText(operation.name),
+ this.modalTranslation.DELETE_BUTTON,
confirmCallback,
- 'Cancel',
+ 'deleteOperationModal'
);
-
- this.modalInstance.instance.open();
}
private createOperation = (operation: OperationModel): void => {
this.ComponentServiceNg2.createInterfaceOperation(this.component, operation).subscribe((response: CreateOperationResponse) => {
this.openOperation = null;
- this.operationList.push(new OperationModel(response));
- this.operationList.sort((a, b) => a.operationType.localeCompare(b.operationType));
+ let curInterf = _.find(
+ this.interfaces,
+ interf => interf.type === operation.interfaceType
+ )
+ if (!curInterf) {
+ curInterf = new UIInterfaceModel({
+ type: response.interfaceType,
+ uniqueId: response.uniqueId,
+ operations: []
+ });
+ this.interfaces.push(curInterf);
+ }
+ curInterf.operations.push(new UIOperationModel(response));
+ this.sortInterfaces();
if (response.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
- const operationId = response.uniqueId;
- const workflowId = response.workflowId;
- const versionId = response.workflowVersionId;
- const artifactId = response.artifactUUID;
- this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, operationId, workflowId, versionId, artifactId).subscribe();
+ this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, response).subscribe();
} else if (operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW) {
this.$state.go('workspace.plugins', { path: 'workflowDesigner' });
}
@@ -188,16 +319,22 @@
private updateOperation = (operation: OperationModel): void => {
this.ComponentServiceNg2.updateInterfaceOperation(this.component, operation).subscribe(newOperation => {
this.openOperation = null;
- const index = _.findIndex(this.operationList, el => el.uniqueId === operation.uniqueId);
- this.operationList.splice(index, 1, newOperation);
- this.component.interfaceOperations = this.operationList;
+
+ _.forEach(this.interfaces, interf => {
+ _.forEach(interf.operations, op => {
+ if (op.uniqueId === newOperation.uniqueId) {
+ const oldIndex = _.findIndex(interf.operations, el => el.uniqueId === op.uniqueId);
+ interf.operations.splice(oldIndex, 1);
+ }
+ })
+ });
+
+ const newInterf = _.find(this.interfaces, interf => interf.type === operation.interfaceType);
+ newInterf.operations.push(new UIOperationModel(newOperation));
+ this.sortInterfaces();
if (newOperation.workflowId && operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING) {
- const operationId = newOperation.uniqueId;
- const workflowId = newOperation.workflowId;
- const versionId = newOperation.workflowVersionId;
- const artifactId = newOperation.artifactUUID;
- this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, operationId, workflowId, versionId, artifactId).subscribe();
+ this.WorkflowServiceNg2.associateWorkflowArtifact(this.component, newOperation).subscribe();
}
});
}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html
index 441875b..81a33c4 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.html
@@ -20,66 +20,92 @@
<form class="w-sdc-form">
<div class="side-by-side">
- <div class="i-sdc-form-item">
- <label class="i-sdc-form-label" [ngClass]="{'required': !isEditMode}">{{ 'OPERATION_NAME' | translate }}</label>
- <input
- type="text"
- name="type"
- data-tests-id="operationType"
- [(ngModel)]="operation.operationType"
- [attr.maxLength]="200"
- [ngClass]="{'disabled':isEditMode}" />
+ <div class="form-item">
+ <sdc-dropdown
+ label="{{ 'OPERATION_INTERFACE_TYPE' | translate }}"
+ [required]="true"
+ testId="interface-name"
+ selectedOption="{{operation.interfaceType}}"
+ placeHolder="Select..."
+ [disabled]="readonly"
+ (changed)="onSelectInterface($event)"
+ [options]="interfaceNames">
+ </sdc-dropdown>
</div>
- <div class="i-sdc-form-item">
- <label class="i-sdc-form-label">{{ 'OPERATION_DESCRIPTION' | translate }}</label>
- <input
- type="text"
- data-tests-id="operationDescription"
- name="description"
- [ngClass]="{'disabled': readonly}"
- [(ngModel)]="operation.description"
- [attr.maxLength]="200" />
+ <div class="form-item" *ngIf="!isInterfaceOther()">
+ <sdc-dropdown
+ label="{{ 'OPERATION_NAME' | translate }}"
+ [required]="true"
+ testId="operation-name"
+ selectedOption="{{operation.name}}"
+ placeHolder="Select..."
+ [disabled]="readonly"
+ (changed)="onSelectOperationName($event)"
+ [options]="operationNames">
+ </sdc-dropdown>
</div>
+ <div class="form-item" *ngIf="isInterfaceOther()">
+ <sdc-input
+ label="{{ 'OPERATION_NAME' | translate }}"
+ [(value)]="operation.name"
+ testId="operationName">
+ </sdc-input>
+ </div>
+
</div>
- <div class="side-by-side association-options">
- <div class="i-sdc-form-item" *ngIf="enableWorkflowAssociation">
- <label class="i-sdc-form-label">{{ 'OPERATION_WORKFLOW_ASSIGNMENT' | translate }}</label>
- <ui-element-dropdown
- data-tests-id="association-type"
- [(value)]="operation.workflowAssociationType"
- [values]="associationOptions"
- (valueChange)="toggleAssociateWorkflow()"
- [readonly]="readonly">
- </ui-element-dropdown>
- </div>
- <div></div>
+ <div class="i-sdc-form-item sdc-input">
+ <span class="sdc-input__label">{{ 'OPERATION_DESCRIPTION' | translate }}</span>
+ <textarea
+ data-tests-id="operationDescription"
+ rows="2"
+ name="description"
+ [(ngModel)]="descriptionValue"
+ [ngClass]="{'disabled': readonly}">
+ </textarea>
</div>
- <div class="side-by-side" *ngIf="isUsingExistingWF()">
- <div class="i-sdc-form-item">
- <label class="i-sdc-form-label required">{{ 'OPERATION_WORKFLOW' | translate }}
+ <div class="side-by-side" *ngIf="enableWorkflowAssociation">
+ <div class="form-item">
+ <sdc-dropdown
+ label="{{ 'OPERATION_WORKFLOW_ASSIGNMENT' | translate }}"
+ placeHolder="Select..."
+ testId="association-type"
+ selectedOption="{{workflowAssociationType}}"
+ [options]="associationOptions"
+ (changed)="toggleAssociateWorkflow($event)"
+ [disabled]="readonly">
+ </sdc-dropdown>
+ </div>
+
+ <div class="form-item" *ngIf="!isUsingExistingWF()"></div>
+
+ <div class="form-item sdc-input" *ngIf="isUsingExistingWF()">
+ <label class="sdc-input__label required">{{ 'OPERATION_WORKFLOW' | translate }}
<span class="archive-warning" *ngIf="archivedWorkflowId === operation.workflowId">({{ 'OPERATION_WORKFLOW_ARCHIVED' | translate }})</span>
+ <span class="no-workflow-warning" *ngIf="!workflows.length">{{ 'OPERATION_NO_WORKFLOW_ERROR' | translate }}</span>
</label>
- <ui-element-dropdown
- data-tests-id="associated-workflow"
- [readonly]="readonly"
- [values]="workflows"
- [(value)]="operation.workflowId"
- (valueChange)="onSelectWorkflow()">
- </ui-element-dropdown>
+ <sdc-dropdown
+ placeHolder="Select..."
+ testId="associated-workflow"
+ selectedOption="{{operation.workflowId}}"
+ [options]="workflows"
+ (changed)="onSelectWorkflow($event)"
+ [disabled]="readonly || !workflows.length">
+ </sdc-dropdown>
</div>
- <div class="i-sdc-form-item">
- <label class="i-sdc-form-label required">{{ 'OPERATION_WORKFLOW_VERSION' | translate }}</label>
- <ui-element-dropdown
- data-tests-id="associated-workflow-version"
- [readonly]="!operation.workflowId || archivedWorkflowId === operation.workflowId || readonly"
- [values]="workflowVersions"
- [(value)]="operation.workflowVersionId"
- (valueChange)="changeWorkflowVersion()">
- </ui-element-dropdown>
+ <div class="form-item sdc-input" *ngIf="isUsingExistingWF()">
+ <sdc-dropdown
+ *ngIf="workflows.length"
+ label="{{ 'OPERATION_WORKFLOW_VERSION' | translate }}"
+ testId="associated-workflow-version"
+ selectedOption="{{operation.workflowVersionId}}"
+ [options]="workflowVersions"
+ (changed)="changeWorkflowVersion($event)"
+ [disabled]="!operation.workflowId || archivedWorkflowId === operation.workflowId || readonly">
+ </sdc-dropdown>
</div>
</div>
@@ -93,7 +119,7 @@
*ngIf="!isUsingExistingWF() && !readonly"
data-tests-id="addInputParameter"
[ngClass]="{'disabled':!canAdd()}"
- (click)="addParam()">{{ 'OPERATION_ADD_PARAMS' | translate }}</a>
+ (click)="addParam()">{{ currentTab === TYPE_INPUT ? 'Add Input' : 'Add Output' }}</a>
</div>
<div class="generic-table">
@@ -120,7 +146,6 @@
<span class="bold-message">{{ 'EMPTY_PARAM_TABLE_NO_SELECTED_WORKFLOW_1' | translate }}</span>
<span>{{ 'EMPTY_PARAM_TABLE_NO_SELECTED_WORKFLOW_2' | translate }}</span>
</div>
- <div *ngIf="!workflows.length" [innerHTML]="'EMPTY_PARAM_TABLE_NO_WORKFLOWS' | translate"></div>
</div>
</div>
@@ -132,7 +157,8 @@
[param]="param"
[inputProps]="inputProperties"
[onRemoveParam]="onRemoveParam"
- [readonly]="readonly">
+ [readonly]="readonly"
+ [validityChanged]="validityChanged">
</param-row>
</div>
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less
index 10976ef..1d65d98 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.less
@@ -1,4 +1,5 @@
@import '../../../../../assets/styles/variables.less';
+@import '../../../../../assets/styles/override.less';
.operation-creator {
font-family: @font-opensans-regular;
@@ -10,32 +11,66 @@
font-size: 12px;
}
- .w-sdc-form .i-sdc-form-item {
+ .w-sdc-form .form-item {
margin-bottom: 15px;
}
+ textarea {
+ min-height: 74px;
+ margin-bottom: 18px;
+ }
+
+ /deep/ .sdc-dropdown__component-container {
+ .sdc-dropdown__header {
+ height: 38px;
+ line-height: 35px;
+
+ svg-icon {
+ margin: 13px 6px;
+ }
+ }
+ }
+
+ /deep/ .sdc-input {
+ margin-bottom: 0;
+
+ .sdc-input__input {
+ height: 38px;
+ }
+ }
+
.side-by-side {
display: flex;
- &.association-options {
- margin-top: 5px;
- }
-
- .i-sdc-form-item {
- flex-basis: 100%;
+ .form-item {
+ flex: 1;
&:first-child {
- flex-basis: 40%;
- margin-right: 10px;
+ margin-right: 14px;
+ flex-basis: 37%;
+ flex-grow: 0;
+ flex-shrink: 0;
}
- .archive-warning {
- font-family: @font-opensans-bold;
- color: @main_color_i;
+ &:nth-child(3) {
+ margin-left: 14px;
+ flex: 0.4;
}
+
}
}
+ .archive-warning {
+ font-family: @font-opensans-bold;
+ color: @main_color_i;
+ }
+
+ .no-workflow-warning {
+ font-family: @font-opensans-bold;
+ color: @sdcui_color_red;
+ float: right;
+ }
+
.input-param-title {
font-size: 16px;
text-transform: uppercase;
@@ -51,24 +86,33 @@
cursor: pointer;
}
}
+
+ .tab {
+ width: 84px;
+ text-align: center;
+ }
}
.generic-table {
- max-height: 233px;
+ max-height: 244px;
min-height: 91px;
background: @main_color_p;
.header-row .header-cell {
- &.remove {
- padding: 8px;
- }
.info-icon {
float: right;
+ position: relative;
+ top: 2px;
}
/deep/ .tooltip-inner {
- max-width: 280px;
+ padding: 2px;
+ max-width: 270px;
font-size: 11px;
}
+ &.remove {
+ padding: 10px;
+ font-size: 10px;
+ }
}
.data-row {
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts
index a304f1a..e1b2b4e 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.component.ts
@@ -7,15 +7,37 @@
import {WorkflowServiceNg2} from 'app/ng2/services/workflow.service';
import {OperationModel, OperationParameter, InputBEModel, RadioButtonModel, WORKFLOW_ASSOCIATION_OPTIONS} from 'app/models';
+import {IDropDownOption} from "sdc-ui/lib/angular/form-elements/dropdown/dropdown-models";
import {Tabs, Tab} from "app/ng2/components/ui/tabs/tabs.component";
import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
+export class DropDownOption implements IDropDownOption {
+ value: string;
+ label: string;
+
+ constructor(value: string, label?: string) {
+ this.value = value;
+ this.label = label || value;
+ }
+}
+
+class TypedDropDownOption extends DropDownOption {
+ type: number;
+
+ constructor(value: string, label?: string, type?: number) {
+ super(value, label);
+ this.type = type;
+ }
+}
+
export interface OperationCreatorInput {
- operation: OperationModel,
+ inputOperation: OperationModel,
inputProperties: Array<InputBEModel>,
enableWorkflowAssociation: boolean,
readonly: boolean,
- isService: boolean
+ isService: boolean,
+ interfaceTypes: { [interfaceType: string]: Array<string> },
+ validityChangedCallback: Function
}
@Component({
@@ -28,7 +50,12 @@
export class OperationCreatorComponent {
input: OperationCreatorInput;
+ inputOperation: OperationModel;
operation: OperationModel;
+ interfaceNames: Array<TypedDropDownOption> = [];
+ interfaceTypes: { [interfaceType: string]: Array<string> };
+ operationNames: Array<DropDownOption> = [];
+ validityChangedCallback: Function;
workflows: Array<DropdownValue> = [];
workflowVersions: Array<DropdownValue> = [];
@@ -45,7 +72,8 @@
tableParameters: Array<OperationParameter> = [];
- associationOptions: Array<DropdownValue>;
+ associationOptions: Array<DropdownValue> = [];
+ workflowAssociationType: string;
enableWorkflowAssociation: boolean;
isEditMode: boolean = false;
@@ -57,6 +85,9 @@
TYPE_INPUT = 'Inputs';
TYPE_OUTPUT = 'Outputs';
+ INTERFACE_OTHER_HEADER = 'Local Interfaces';
+ INTERFACE_OTHER = 'Local';
+
@ViewChild('propertyInputTabs') propertyInputTabs: Tabs;
currentTab: String;
@@ -65,24 +96,41 @@
this.propertyTooltipText = this.translateService.translate("OPERATION_PROPERTY_TOOLTIP_TEXT");
this.associationOptions = [
- new DropdownValue(WORKFLOW_ASSOCIATION_OPTIONS.NONE, this.translateService.translate("NO_WORKFLOW_ASSOCIATION")),
- new DropdownValue(WORKFLOW_ASSOCIATION_OPTIONS.EXISTING, this.translateService.translate("EXISTING_WORKFLOW_ASSOCIATION"))
+ new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.NONE, this.translateService.translate("NO_WORKFLOW_ASSOCIATION")),
+ new DropDownOption(WORKFLOW_ASSOCIATION_OPTIONS.EXISTING, this.translateService.translate("EXISTING_WORKFLOW_ASSOCIATION"))
];
+
+ this.workflowAssociationType = this.operation.workflowAssociationType || WORKFLOW_ASSOCIATION_OPTIONS.NONE;
});
this.currentTab = this.TYPE_INPUT;
}
- ngOnInit() {
- this.readonly = this.input.readonly;
- this.enableWorkflowAssociation = this.input.enableWorkflowAssociation;
- this.inputProperties = this.input.inputProperties;
-
- const inputOperation = this.input.operation;
- this.operation = new OperationModel(inputOperation || {});
- if (!inputOperation) {
- this.operation.workflowAssociationType = WORKFLOW_ASSOCIATION_OPTIONS.NONE;
+ createInterfaceDropdown(type: string) {
+ let label = type;
+ const lastDot = label.lastIndexOf('.');
+ if (lastDot > -1) {
+ label = label.substr(lastDot + 1);
}
+ return new TypedDropDownOption(type, label);
+ }
+
+ ngOnInit() {
+ this.interfaceNames = _.map(
+ _.keys(this.interfaceTypes),
+ type => this.createInterfaceDropdown(type)
+ );
+ this.interfaceNames.unshift(new TypedDropDownOption('Existing Interfaces', 'Existing Interfaces', 1));
+ this.interfaceNames = this.interfaceNames.concat([
+ new TypedDropDownOption(' ', ' ', 3),
+ new TypedDropDownOption(this.INTERFACE_OTHER_HEADER, this.INTERFACE_OTHER_HEADER, 1),
+ new TypedDropDownOption(this.INTERFACE_OTHER)
+ ]);
+
+ const inputOperation = this.inputOperation;
+ this.operation = new OperationModel(inputOperation || {});
+ this.onSelectInterface(new DropDownOption(this.operation.interfaceType));
+ this.validityChanged();
if (this.enableWorkflowAssociation) {
this.isLoading = true;
@@ -113,13 +161,16 @@
}
reconstructOperation = () => {
- const inputOperation = this.input.operation;
+ const inputOperation = this.inputOperation;
if (inputOperation) {
if (this.enableWorkflowAssociation && inputOperation.workflowVersionId && this.isUsingExistingWF(inputOperation)) {
- this.onSelectWorkflow(inputOperation.workflowVersionId).add(() => {
- this.buildParams();
- this.updateTable();
- });
+ const sub = this.onSelectWorkflow(new DropDownOption(inputOperation.workflowId), inputOperation.workflowVersionId);
+ if (sub) {
+ sub.add(() => {
+ this.buildParams();
+ this.updateTable();
+ });
+ }
} else {
this.inputParameters = this.noAssignInputParameters;
this.outputParameters = this.noAssignOutputParameters;
@@ -132,14 +183,15 @@
}
}
this.updateTable();
+ this.validityChanged();
}
buildParams = () => {
- if (this.input.operation.outputParams) {
+ if (this.inputOperation.outputs) {
this.currentTab = this.TYPE_OUTPUT;
this.updateTable();
_.forEach(
- [...this.input.operation.outputParams.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
+ [...this.inputOperation.outputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
(output: OperationParameter) => {
this.addParam(output);
}
@@ -147,9 +199,9 @@
}
this.currentTab = this.TYPE_INPUT;
this.updateTable();
- if (this.input.operation.inputParams) {
+ if (this.inputOperation.inputs) {
_.forEach(
- [...this.input.operation.inputParams.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
+ [...this.inputOperation.inputs.listToscaDataDefinition].sort((a, b) => a.name.localeCompare(b.name)),
(input: OperationParameter) => {
this.addParam(input);
}
@@ -157,15 +209,53 @@
}
}
- onSelectWorkflow(selectedVersionId?: string): Subscription {
+ isInterfaceOther(): boolean {
+ return this.operation.interfaceType === this.INTERFACE_OTHER;
+ }
- this.operation.workflowVersionId = selectedVersionId || null;
+ onSelectInterface(interf: IDropDownOption) {
+ if (interf && this.operation.interfaceType !== interf.value) {
+ this.operation.name = undefined;
+ }
+ this.operation.interfaceType = interf && interf.value;
+ this.operationNames = !this.operation.interfaceType ? [] : (
+ _.map(
+ this.interfaceTypes[this.operation.interfaceType],
+ name => new DropDownOption(name)
+ )
+ );
+ this.validityChanged();
+ }
+
+ onSelectOperationName(name: IDropDownOption) {
+ if (name) {
+ this.operation.name = name.value;
+ this.validityChanged();
+ }
+ }
+
+ get descriptionValue() {
+ return this.operation.description;
+ }
+
+ set descriptionValue(v) {
+ this.operation.description = v;
+ this.validityChanged();
+ }
+
+ onSelectWorkflow(workflowId: DropDownOption, selectedVersionId?: string): Subscription {
+
+ if (_.isUndefined(workflowId) || workflowId.value === this.operation.workflowId) {
+ return;
+ }
+ this.operation.workflowId = workflowId.value;
if (!this.assignInputParameters[this.operation.workflowId]) {
this.assignInputParameters[this.operation.workflowId] = {};
this.assignOutputParameters[this.operation.workflowId] = {};
}
this.isLoading = true;
+ this.validityChanged();
return this.workflowServiceNg2.getWorkflowVersions(this.operation.workflowId).subscribe((versions: Array<any>) => {
this.isLoading = false;
@@ -197,18 +287,34 @@
this.operation.workflowVersionId = _.last(this.workflowVersions).value;
}
- this.changeWorkflowVersion();
+ this.changeWorkflowVersion(new DropDownOption(this.operation.workflowVersionId));
+ this.validityChanged();
});
}
- changeWorkflowVersion() {
+ changeWorkflowVersion(versionId: DropDownOption) {
+
+ if (_.isUndefined(versionId)) {
+ return;
+ }
+
+ this.operation.workflowVersionId = versionId.value;
this.inputParameters = this.assignInputParameters[this.operation.workflowId][this.operation.workflowVersionId];
this.outputParameters = this.assignOutputParameters[this.operation.workflowId][this.operation.workflowVersionId];
this.updateTable();
+ this.validityChanged();
+
}
- toggleAssociateWorkflow() {
+ toggleAssociateWorkflow(type: DropDownOption) {
+
+ if (_.isUndefined(type)) {
+ return;
+ }
+
+ this.operation.workflowAssociationType = type.value;
+ this.workflowAssociationType = this.operation.workflowAssociationType;
if (!this.isUsingExistingWF()) {
this.inputParameters = this.noAssignInputParameters;
@@ -224,15 +330,19 @@
}
this.updateTable();
+ this.validityChanged();
}
tabChanged = (event) => {
+
this.currentTab = event.title;
this.updateTable();
+
}
updateTable() {
+
switch (this.currentTab) {
case this.TYPE_INPUT:
this.tableParameters = this.inputParameters;
@@ -241,17 +351,20 @@
this.tableParameters = this.outputParameters;
break;
}
+
}
addParam(param?: OperationParameter): void {
+ this.validityChanged();
this.tableParameters.push(new OperationParameter(param));
}
- canAdd(): boolean {
+ canAdd = (): boolean => {
+
let valid = true;
if (this.currentTab === this.TYPE_INPUT) {
_.forEach(this.inputParameters, param => {
- if (!param.name || !param.property) {
+ if (!param.name || !param.inputId) {
valid = false;
}
});
@@ -262,13 +375,16 @@
}
});
}
+
return valid;
+
}
- isParamsValid(): boolean {
+ isParamsValid = (): boolean => {
+
let valid = true;
_.forEach(this.inputParameters, param => {
- if (!param.name || !param.property) {
+ if (!param.name || !param.inputId) {
valid = false;
}
});
@@ -277,7 +393,9 @@
valid = false;
}
});
+
return valid;
+
}
onRemoveParam = (param: OperationParameter): void => {
@@ -285,9 +403,9 @@
this.tableParameters.splice(index, 1);
}
- createParamLists(): void {
- this.operation.createInputParamsList(this.inputParameters);
- this.operation.createOutputParamsList(this.outputParameters);
+ createParamLists = () => {
+ this.operation.createInputsList(this.inputParameters);
+ this.operation.createOutputsList(this.outputParameters);
}
isUsingExistingWF = (operation?: OperationModel): boolean => {
@@ -295,15 +413,20 @@
return operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.EXISTING;
}
- shouldCreateWF(operation?: OperationModel): boolean {
+ shouldCreateWF = (operation?: OperationModel): boolean => {
operation = operation || this.operation;
return this.operation.workflowAssociationType === WORKFLOW_ASSOCIATION_OPTIONS.NEW;
}
- checkFormValidForSubmit(): boolean {
- return this.operation.operationType &&
+ checkFormValidForSubmit = (): boolean => {
+ return this.operation.name &&
(!this.isUsingExistingWF() || this.operation.workflowVersionId) &&
this.isParamsValid();
}
+ validityChanged = () => {
+ let validState = this.checkFormValidForSubmit();
+ this.validityChangedCallback(validState);
+ }
+
}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts
index 7d88123..0b6f833 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/operation-creator.module.ts
@@ -3,8 +3,9 @@
import {FormsModule} from "@angular/forms";
import {FormElementsModule} from "app/ng2/components/ui/form-components/form-elements.module";
-import {TranslateModule} from "app/ng2/shared/translator/translate.module";
+import {SdcUiComponentsModule} from "sdc-ui/lib/angular/index";
import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module";
+import {TranslateModule} from "app/ng2/shared/translator/translate.module";
import {OperationCreatorComponent} from "./operation-creator.component";
import {ParamRowComponent} from './param-row/param-row.component';
@@ -16,6 +17,7 @@
],
imports: [
CommonModule,
+ SdcUiComponentsModule,
FormsModule,
FormElementsModule,
TranslateModule,
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html
index 9a5c101..1128d60 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.html
@@ -19,6 +19,7 @@
*ngIf="!isAssociateWorkflow"
data-tests-id="paramName"
[(value)]="param.name"
+ (valueChange)="onChangeName()"
[readonly]="readonly">
</ui-element-input>
<span *ngIf="isAssociateWorkflow">{{param.name}}</span>
@@ -41,13 +42,14 @@
*ngIf="filteredInputProps.length || !isAssociateWorkflow"
data-tests-id="paramProperty"
[values]="filteredInputProps"
- [(value)]="param.property"
+ value="paramId"
+ (valueChange)="onChangeProperty($event)"
[readonly]="readonly">
</ui-element-dropdown>
<span
*ngIf="!filteredInputProps.length && isAssociateWorkflow"
class="no-properties-error">
- No available properties of this type.
+ {{ 'PARAM_NONE_OF_TYPE' | translate }}
</span>
</div>
@@ -55,15 +57,18 @@
<checkbox
*ngIf="!isAssociateWorkflow"
data-tests-id="paramMandatory"
- [(checked)]="param.mandatory"
+ [(checked)]="param.required"
[ngClass]="{'disabled':readonly}">
</checkbox>
</div>
<div class="cell remove" *ngIf="!isAssociateWorkflow && !readonly">
- <span
- class="sprite-new delete-item-icon"
- data-tests-id="removeInputParam"
- (click)="onRemoveParam(param)">
- </span>
+ <svg-icon
+ name="trash-o"
+ mode="info"
+ size="small"
+ testId="removeInputParam"
+ (click)="onRemoveParam(param)"
+ [clickable]="true">
+ </svg-icon>
</div>
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less
index 2c2625d..99a54bb 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.less
@@ -5,7 +5,10 @@
align-items: center;
justify-content: center;
- .delete-item-icon {
+ svg-icon {
+ position: relative;
+ right: -3px;
+
&:hover {
cursor: pointer;
}
diff --git a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts
index a12425d..8837a17 100644
--- a/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts
+++ b/catalog-ui/src/app/ng2/pages/interface-operation/operation-creator/param-row/param-row.component.ts
@@ -1,6 +1,7 @@
import {Component, Input} from '@angular/core';
import {DataTypeService} from "app/ng2/services/data-type.service";
import {OperationParameter, InputBEModel} from 'app/models';
+import {DropDownOption} from "../operation-creator.component";
import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component";
@Component({
@@ -16,8 +17,10 @@
@Input() isAssociateWorkflow: boolean;
@Input() readonly: boolean;
@Input() isInputParam: boolean;
+ @Input() validityChanged: Function;
- propTypeEnum: Array<String> = [];
+ paramId: string;
+ propTypeEnum: Array<DropDownOption> = [];
filteredInputProps: Array<DropdownValue> = [];
constructor(private dataTypeService: DataTypeService) {}
@@ -26,20 +29,48 @@
this.propTypeEnum = _.uniq(
_.map(
this.getPrimitiveSubtypes(),
- prop => prop.type
+ prop => new DropDownOption(prop.type)
)
);
this.onChangeType();
+ this.validityChanged();
}
- onChangeType() {
+ onChangeName() {
+ this.validityChanged();
+ }
+
+ onChangeType(paramId?: string) {
this.filteredInputProps = _.map(
_.filter(
this.getPrimitiveSubtypes(),
- prop => prop.type === this.param.type
+ prop => !this.param.type || prop.type === this.param.type
),
prop => new DropdownValue(prop.uniqueId, prop.name)
);
+ if (paramId) {
+ this.paramId = paramId;
+ }
+ }
+
+ onChangeProperty(paramId: string) {
+ this.param.inputId = paramId;
+ const newProp = _.find(
+ this.getPrimitiveSubtypes(),
+ prop => this.param.inputId === prop.uniqueId
+ );
+
+ if (!this.param.type) {
+ this.param.type = newProp.type;
+ this.onChangeType(paramId);
+ } else {
+ this.paramId = paramId;
+ }
+
+ if (!this.param.name) {
+ this.param.name = newProp.name;
+ }
+ this.validityChanged();
}
getPrimitiveSubtypes(): Array<InputBEModel> {
diff --git a/catalog-ui/src/app/ng2/services/component-services/component.service.ts b/catalog-ui/src/app/ng2/services/component-services/component.service.ts
index 26b0291..97e62da 100644
--- a/catalog-ui/src/app/ng2/services/component-services/component.service.ts
+++ b/catalog-ui/src/app/ng2/services/component-services/component.service.ts
@@ -24,9 +24,9 @@
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
import {Response, URLSearchParams} from '@angular/http';
-import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, OperationModel, CreateOperationResponse} from "app/models";
+import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, OperationModel, BEOperationModel, CreateOperationResponse} from "app/models";
import {downgradeInjectable} from '@angular/upgrade/static';
-import {COMPONENT_FIELDS} from "app/utils";
+import {COMPONENT_FIELDS, CommonUtils} from "app/utils";
import {ComponentGenericResponse} from "../responses/component-generic-response";
import {InstanceBePropertiesMap} from "../../../models/properties-inputs/property-fe-map";
import {API_QUERY_PARAMS} from "app/utils";
@@ -122,38 +122,79 @@
return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_PROPERTIES]);
}
- getInterfaceOperations(component:Component):Observable<ComponentGenericResponse> {
+ getInterfaces(component:Component):Observable<ComponentGenericResponse> {
return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INTERFACE_OPERATIONS]);
}
getInterfaceOperation(component:Component, operation:OperationModel):Observable<OperationModel> {
- return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId)
- .map((res:Response) => res.json());
+ return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId)
+ .map((res:Response) => {
+ return res.json();
+ });
}
createInterfaceOperation(component:Component, operation:OperationModel):Observable<CreateOperationResponse> {
const operationList = {
- 'interfaceOperations': {
- [operation.operationType]: operation
+ 'interfaces': {
+ [operation.interfaceType]: {
+ 'type': operation.interfaceType,
+ 'operations': {
+ [operation.name]: new BEOperationModel(operation)
+ }
+ }
}
};
return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList)
- .map((res:Response) => res.json());
+ .map((res:Response) => {
+ const interf = _.find(res.json().interfaces, (interf: any) => interf.type === operation.interfaceType);
+ const newOperation = _.find(interf.operations, (op:OperationModel) => op.name === operation.name);
+ return new CreateOperationResponse({
+ ...newOperation,
+ interfaceType: interf.type,
+ interfaceId: interf.uniqueId
+ });
+ });
}
updateInterfaceOperation(component:Component, operation:OperationModel):Observable<CreateOperationResponse> {
const operationList = {
- 'interfaceOperations': {
- [operation.operationType]: operation
+ 'interfaces': {
+ [operation.interfaceType]: {
+ 'type': operation.interfaceType,
+ 'operations': {
+ [operation.name]: new BEOperationModel(operation)
+ }
+ }
}
};
return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations', operationList)
- .map((res:Response) => res.json());
+ .map((res:Response) => {
+ const interf = _.find(res.json().interfaces, (interf: any) => interf.type === operation.interfaceType);
+ const newOperation = _.find(interf.operations, (op:OperationModel) => op.name === operation.name);
+ return new CreateOperationResponse({
+ ...newOperation,
+ interfaceType: interf.type,
+ interfaceId: interf.uniqueId
+ });
+ });
}
deleteInterfaceOperation(component:Component, operation:OperationModel):Observable<OperationModel> {
- return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaceOperations/' + operation.uniqueId)
- .map((res:Response) => res.json());
+ return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId)
+ .map((res:Response) => {
+ return res.json();
+ });
+ }
+
+ getInterfaceTypes(component:Component):Observable<{[id:string]: Array<string>}> {
+ return this.http.get(this.baseUrl + 'interfaceLifecycleTypes')
+ .map((res:Response) => {
+ const interfaceMap = {};
+ _.forEach(res.json(), (interf:any) => {
+ interfaceMap[interf.toscaPresentation.type] = _.keys(interf.toscaPresentation.operations);
+ });
+ return interfaceMap;
+ });
}
getCapabilitiesAndRequirements(componentType: string, componentId:string):Observable<ComponentGenericResponse> {
diff --git a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
index a77133e..647cc92 100644
--- a/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
+++ b/catalog-ui/src/app/ng2/services/responses/component-generic-response.ts
@@ -92,6 +92,7 @@
this.toscaArtifacts = new ArtifactGroupModel(response.toscaArtifacts);
}
if(response.interfaces) {
+ this.interfaces = CommonUtils.initInterfaces(response.interfaces);
this.interfaceOperations = CommonUtils.initInterfaceOperations(response.interfaces);
}
if(response.metadata) {
diff --git a/catalog-ui/src/app/ng2/services/workflow.service.ts b/catalog-ui/src/app/ng2/services/workflow.service.ts
index 1fc5c78..36d56d6 100644
--- a/catalog-ui/src/app/ng2/services/workflow.service.ts
+++ b/catalog-ui/src/app/ng2/services/workflow.service.ts
@@ -3,7 +3,17 @@
import { Observable } from "rxjs/Observable";
import { HttpService } from "./http.service";
import { SdcConfigToken, ISdcConfig } from "../config/sdc-config.config";
-import { Component } from "app/models";
+import { Component, CreateOperationResponse } from "app/models";
+
+interface WorkflowOutputParameter {
+ name: string,
+ type: string,
+ mandatory: boolean
+}
+
+interface WorkflowInputParameter extends WorkflowOutputParameter {
+ property: string;
+}
@Injectable()
export class WorkflowServiceNg2 {
@@ -20,9 +30,9 @@
this.catalogBaseUrl = sdcConfig.api.POST_workflow_artifact;
}
- public associateWorkflowArtifact(component: Component, operationId: string, workflowId: string, workflowVersionId: string, artifactUuid: string): Observable<any> {
- return this.http.post(this.baseUrl + '/workflows/' + workflowId + '/versions/' + workflowVersionId + '/artifact-deliveries', {
- endpoint: this.catalogBaseUrl + '/' + component.getTypeUrl() + component.uuid + '/interfaces/' + operationId + '/artifacts/' + artifactUuid,
+ public associateWorkflowArtifact(component: Component, operation: CreateOperationResponse): Observable<any> {
+ return this.http.post(this.baseUrl + '/workflows/' + operation.workflowId + '/versions/' + operation.workflowVersionId + '/artifact-deliveries', {
+ endpoint: this.catalogBaseUrl + '/' + component.getTypeUrl() + component.uuid + '/interfaces/' + operation.interfaceId + '/operations/' + operation.uniqueId + '/artifacts/' + operation.artifactUUID,
method: 'POST'
})
.map((res:Response) => {
@@ -40,7 +50,7 @@
public getWorkflowVersions(workflowId: string, filterCertified: boolean = true): Observable<any> {
return this.http.get(this.baseUrl + '/workflows/' + workflowId + '/versions' + (filterCertified ? '?state=' + this.VERSION_STATE_CERTIFIED : ''))
.map((res:Response) => {
- return res.json().items;
+ return _.map(res.json().items, version => version);
});
}
diff --git a/catalog-ui/src/app/utils/common-utils.ts b/catalog-ui/src/app/utils/common-utils.ts
index 7ba50fd..c5259f0 100644
--- a/catalog-ui/src/app/utils/common-utils.ts
+++ b/catalog-ui/src/app/utils/common-utils.ts
@@ -19,9 +19,9 @@
*/
import * as _ from "lodash";
-import {Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel, OperationModel} from "../models";
-import {ComponentInstanceFactory} from "./component-instance-factory";
-import {InputBEModel, PropertyBEModel, RelationshipModel} from "app/models";
+import { ComponentInstanceFactory } from "./component-instance-factory";
+import { Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel, InterfaceModel, OperationModel } from "../models";
+import { InputBEModel, PropertyBEModel, RelationshipModel } from "app/models";
import { PolicyInstance } from "app/models/graph/zones/policy-instance";
import { GroupInstance } from "../models/graph/zones/group-instance";
@@ -139,44 +139,55 @@
return groups;
}
- static initInterfaceOperations(interfaces: any): Array<OperationModel> {
+ static initInterfaces(interfaces: Array<InterfaceModel>): Array<InterfaceModel> {
- return _.reduce(interfaces, (acc, interf: any) => {
+ return _.map(interfaces, (interf: InterfaceModel) => {
+
+ return new InterfaceModel({
+ type: interf.type,
+ uniqueId: interf.uniqueId,
+ operations: _.map(interf.operations,
+ (operation: OperationModel) => {
+ const newOperation = new OperationModel(operation);
+ newOperation.interfaceType = interf.type;
+ newOperation.interfaceId = interf.uniqueId;
+
+ const {inputs, outputs} = operation;
+ if (inputs) {
+ newOperation.createInputsList(inputs.listToscaDataDefinition);
+ }
+ if (outputs) {
+ newOperation.createOutputsList(outputs.listToscaDataDefinition);
+ }
+
+ return newOperation;
+ }
+ )
+ });
+
+ });
+ }
+
+ static initInterfaceOperations(interfaces: Array<InterfaceModel>): Array<OperationModel> {
+
+ return _.reduce(interfaces, (acc, interf: InterfaceModel) => {
return acc.concat(
_.map(interf.operations,
- ({description, name, uniqueId, inputs, outputs, workflowId, workflowVersionId, workflowAssociationType}) => {
- const operation = new OperationModel({
- description,
- operationType: name,
- uniqueId,
- workflowAssociationType,
- workflowId,
- workflowVersionId
- });
+ (operation: OperationModel) => {
+ const newOperation = new OperationModel(operation);
+ newOperation.interfaceType = interf.type;
+ newOperation.interfaceId = interf.uniqueId;
+
+ const {inputs, outputs} = operation;
if (inputs) {
- const inputParams = _.map(inputs.listToscaDataDefinition, (input:any) => {
- return {
- name: input.name,
- property: input.inputId,
- type: input.type,
- mandatory: input.mandatory
- };
- });
- operation.createInputParamsList(inputParams);
+ newOperation.createInputsList(inputs.listToscaDataDefinition);
}
if (outputs) {
- const outputParams = _.map(outputs.listToscaDataDefinition, (output:any) => {
- return {
- name: output.name,
- property: output.inputId,
- type: output.type,
- mandatory: output.mandatory
- };
- });
- operation.createOutputParamsList(outputParams);
+ newOperation.createOutputsList(outputs.listToscaDataDefinition);
}
- return operation;
+
+ return newOperation;
}
)
);