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