diff options
author | miriame <miriam.eini@amdocs.com> | 2019-02-13 15:17:26 +0200 |
---|---|---|
committer | miriame <miriam.eini@amdocs.com> | 2019-02-14 10:45:13 +0200 |
commit | c2ce914541e694c7d1c8853b88936095e8b9ede4 (patch) | |
tree | d9d6dfa4b011c6868048d83a2116743b283b1cb1 /catalog-ui/src | |
parent | d72aaa18d4dbfb8017dce566d9c41ccc00985528 (diff) |
Add 'Service Dependencies' tab in composition page
Issue-ID: SDC-1987
Change-Id: Ib5b688c12234c81fe6f89b2b5d37dd16a75b0db9
Signed-off-by: miriame <miriam.eini@amdocs.com>
Diffstat (limited to 'catalog-ui/src')
37 files changed, 1274 insertions, 53 deletions
diff --git a/catalog-ui/src/app/app.ts b/catalog-ui/src/app/app.ts index 2f7b78169e..8fa7f1eb50 100644 --- a/catalog-ui/src/app/app.ts +++ b/catalog-ui/src/app/app.ts @@ -517,6 +517,14 @@ ng1appModule.config([ controller: viewModelsModuleName + '.ResourceArtifactsViewModel' } ); + $stateProvider.state( + 'workspace.composition.dependencies', { + url: 'dependencies', + parent: 'workspace.composition', + templateUrl: './view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view.html', + controller: viewModelsModuleName + '.ServiceDependenciesViewModel' + } + ); $stateProvider.state( States.WORKSPACE_INTERFACE_OPERATION, { diff --git a/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts b/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts index 194845c238..dd50524444 100644 --- a/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts +++ b/catalog-ui/src/app/directives/graphs-v2/common/style/component-instances-nodes-style.ts @@ -237,7 +237,27 @@ export class ComponentInstanceNodesStyle { css: { 'shape': 'rectangle', 'background-image': (ele:Cy.Collection) => { - return ele.data().setUncertifiedImageBgStyle(ele, GraphUIObjects.NODE_OVERLAP_MIN_SIZE);//Change name to setUncertifiedImageBgStyle?? + return ele.data().initUncertifiedImage(ele, GraphUIObjects.NODE_OVERLAP_MIN_SIZE); + }, + "border-width": 0 + } + }, + { + selector: '.dependent', + css: { + 'shape': 'rectangle', + 'background-image': (ele:Cy.Collection) => { + return ele.data().initDependentImage(ele, GraphUIObjects.NODE_OVERLAP_MIN_SIZE) + }, + "border-width": 0 + } + }, + { + selector: '.dependent.not-certified', + css: { + 'shape': 'rectangle', + 'background-image': (ele:Cy.Collection) => { + return ele.data().initUncertifiedDependentImage(ele, GraphUIObjects.NODE_OVERLAP_MIN_SIZE) }, "border-width": 0 } diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts index 804e772ac4..502188b3c2 100644 --- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.directive.ts @@ -35,7 +35,7 @@ import { NodesFactory, Point } from "app/models"; -import { ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors } from "app/utils"; +import { ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors, DEPENDENCY_EVENTS } from "app/utils"; import { EventListenerService, LoaderService } from "app/services"; import { CompositionGraphLinkUtils } from "./utils/composition-graph-links-utils"; import { CompositionGraphGeneralUtils } from "./utils/composition-graph-general-utils"; @@ -185,6 +185,7 @@ export class CompositionGraph implements ng.IDirective { }); this.eventListenerService.unRegisterObserver(EVENTS.SHOW_LOADER_EVENT + 'composition-graph'); this.eventListenerService.unRegisterObserver(EVENTS.HIDE_LOADER_EVENT + 'composition-graph'); + this.eventListenerService.unRegisterObserver(DEPENDENCY_EVENTS.ON_DEPENDENCY_CHANGE); }); }; @@ -383,6 +384,15 @@ export class CompositionGraph implements ng.IDirective { this.loadGraphData(scope); }); + this.eventListenerService.registerObserverCallback(DEPENDENCY_EVENTS.ON_DEPENDENCY_CHANGE, (ischecked: boolean) => { + if (ischecked) { + this._cy.$('node:selected').addClass('dependent'); + } else { + // due to defect in cytoscape, just changing the class does not replace the icon, and i need to revert to original icon with no markings. + this._cy.$('node:selected').removeClass('dependent'); + this._cy.$('node:selected').style({'background-image': this._cy.$('node:selected').data('originalImg')}); + } + }); scope.zoom = (zoomIn: boolean): void => { let currentZoom: number = this._cy.zoom(); diff --git a/catalog-ui/src/app/models.ts b/catalog-ui/src/app/models.ts index e5cc483b63..3a31cafab5 100644 --- a/catalog-ui/src/app/models.ts +++ b/catalog-ui/src/app/models.ts @@ -114,4 +114,5 @@ export * from './models/wizard-step'; export * from './models/radio-button'; export * from './models/filter-properties-assignment-data' export * from './models/properties-inputs/input-be-model' +export * from './models/service-instance-properties' diff --git a/catalog-ui/src/app/models/componentsInstances/componentInstance.ts b/catalog-ui/src/app/models/componentsInstances/componentInstance.ts index fcc3298249..f95d6553cd 100644 --- a/catalog-ui/src/app/models/componentsInstances/componentInstance.ts +++ b/catalog-ui/src/app/models/componentsInstances/componentInstance.ts @@ -61,6 +61,11 @@ export class ComponentInstance { public groupInstances:Array<Module>; public invariantName:string; public originArchived:boolean; + public directives: Array<string>; + + DIRECTIVES_TYPES = { + SELECTABLE: 'selectable' + }; constructor(componentInstance?:ComponentInstance) { @@ -92,6 +97,7 @@ export class ComponentInstance { this.sourceModelUid = componentInstance.sourceModelUid; this.sourceModelUuid = componentInstance.sourceModelUuid; this.originArchived = componentInstance.originArchived; + this.directives = componentInstance.directives; } } @@ -177,4 +183,19 @@ export class ComponentInstance { public get iconClass() { return this.iconSprite + ' ' + this.icon; } + + public isDependent = () : boolean => { + return this.directives && this.directives.indexOf(this.DIRECTIVES_TYPES.SELECTABLE) !== -1; + } + + public markAsDependent = () : void => { + this.directives.push(this.DIRECTIVES_TYPES.SELECTABLE); + } + + public unmarkAsDependent = () : void => { + let index = this.directives.indexOf(this.DIRECTIVES_TYPES.SELECTABLE); + if(index >= 0) { + this.directives.splice(index, 1); + } + } } diff --git a/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts b/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts index a24142348c..51a0c7b3e5 100644 --- a/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts +++ b/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-base.ts @@ -48,33 +48,31 @@ export abstract class CompositionCiNodeBase extends CommonCINodeBase implements this.isUcpePart = false; this.isInsideGroup = false; } - - - public setUncertifiedImageBgStyle(node:Cy.Collection, nodeMinSize:number):string { - let uncertifiedIconWidth:number = GraphUIObjects.HANDLE_SIZE; + + protected enhanceImage(node:Cy.Collection, nodeMinSize:number, imgUrl: string):string { + let infoIconWidth:number = GraphUIObjects.HANDLE_SIZE; let nodeWidth:number = node.data('imgWidth') || node.width(); - let uncertifiedCanvasWidth: number = nodeWidth; + let infoCanvasWidth: number = nodeWidth; - if (nodeWidth < nodeMinSize) { //uncertified icon will overlap too much of the node, need to expand canvas. - uncertifiedCanvasWidth = nodeWidth + uncertifiedIconWidth/2; //expand canvas so that only half of the icon overlaps with the node + if (nodeWidth < nodeMinSize) { //info icon will overlap too much of the node, need to expand canvas. + infoCanvasWidth = nodeWidth + infoIconWidth/2; //expand canvas so that only half of the icon overlaps with the node } - const x = uncertifiedCanvasWidth - nodeWidth, y = x, width = nodeWidth, height = width; + const x = infoCanvasWidth - nodeWidth, y = x, width = nodeWidth, height = width; const canvasImages:ICanvasImage[] = [ { src: this.imagesPath + this.componentInstance.icon + '.png', x, y, width, height}, - { src: this.imagesPath + 'uncertified.png', x: 0, y: 0, width: uncertifiedIconWidth, height: uncertifiedIconWidth} + { src: imgUrl, x: 0, y: 0, width: infoIconWidth, height: infoIconWidth} ]; - //Create the image and update the node background styles - this.imageCreator.getMultiLayerBase64Image(canvasImages, uncertifiedCanvasWidth, uncertifiedCanvasWidth).then(img => this.updateNodeStyles(node,uncertifiedCanvasWidth,img)); + this.imageCreator.getMultiLayerBase64Image(canvasImages, infoCanvasWidth, infoCanvasWidth).then(img => this.updateNodeStyles(node,infoCanvasWidth,img)); return this.img; // Return the referance to the image (in Base64 format) } - - public setArchivedImageBgStyle(node:Cy.Collection, nodeMinSize:number):string { + + public setArchivedImageBgStyle(node:Cy.Collection, nodeMinSize:number):string { let archivedIconWidth:number = GraphUIObjects.HANDLE_SIZE; let nodeWidth:number = node.data('imgWidth') || node.width(); let archivedCanvasWidth: number = nodeWidth; @@ -92,6 +90,10 @@ export abstract class CompositionCiNodeBase extends CommonCINodeBase implements return this.img; // Return the default img } + public initUncertifiedImage(node:Cy.Collection, nodeMinSize:number):string { + return this.enhanceImage(node, nodeMinSize, this.imagesPath + 'uncertified.png'); + } + protected getDisplayName():string { let graphResourceName = AngularJSBridge.getFilter('graphResourceName'); let resourceName = AngularJSBridge.getFilter('resourceName'); @@ -99,7 +101,7 @@ export abstract class CompositionCiNodeBase extends CommonCINodeBase implements } //TODO:: move to Base class ??? - private updateNodeStyles(node,canvasWidth,imageBase64){ + private updateNodeStyles(node,canvasWidth,imageBase64){ this.img = imageBase64; node.style({ 'background-image': this.img, @@ -107,7 +109,7 @@ export abstract class CompositionCiNodeBase extends CommonCINodeBase implements 'background-height': canvasWidth, 'background-position-x':0, 'background-position-y':0 - }); + }); } } diff --git a/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-service-proxy.ts b/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-service-proxy.ts index b025221f25..5ef3a733b2 100644 --- a/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-service-proxy.ts +++ b/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-service-proxy.ts @@ -18,29 +18,45 @@ * ============LICENSE_END========================================================= */ -import { ImagesUrl, GraphUIObjects} from "../../../../utils/constants"; -import {ComponentInstance, CompositionCiNodeBase} from "../../../../models"; -import {ImageCreatorService} from "../../../../directives/graphs-v2/image-creator/image-creator.service"; +import {ComponentInstance, CompositionCiNodeBase} from "app/models"; +import {ImageCreatorService} from "app/directives/graphs-v2/image-creator/image-creator.service"; +import {ImagesUrl, GraphUIObjects} from "app/utils"; export class CompositionCiNodeServiceProxy extends CompositionCiNodeBase { + private isDependent: boolean; + private originalImg: string; constructor(instance:ComponentInstance, imageCreator:ImageCreatorService) { super(instance, imageCreator); + this.isDependent =instance.isDependent(); this.initService(); } private initService():void { this.imagesPath = this.imagesPath + ImagesUrl.SERVICE_PROXY_ICONS; this.img = this.imagesPath + this.componentInstance.icon + '.png'; + this.originalImg = this.img; this.imgWidth = GraphUIObjects.DEFAULT_RESOURCE_WIDTH; this.classes = 'service-node'; if(this.archived){ this.classes = this.classes + ' archived'; return; } + if (this.isDependent) { + this.classes += ' dependent'; + } if (!this.certified) { this.classes = this.classes + ' not-certified'; } } + + public initUncertifiedDependentImage(node:Cy.Collection, nodeMinSize:number):string { + return this.enhanceImage(node, nodeMinSize, this.imagesPath + 'uncertified_dependent.png'); + } + + public initDependentImage(node:Cy.Collection, nodeMinSize:number):string { + return this.enhanceImage(node, nodeMinSize, this.imagesPath + 'dependent.png'); + } + } diff --git a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts index 14b6385f2f..aa68551950 100644 --- a/catalog-ui/src/app/models/properties-inputs/property-be-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/property-be-model.ts @@ -39,6 +39,7 @@ export class PropertyBEModel { password: boolean; required: boolean; schema: SchemaPropertyGroupModel; + schemaType: string; type: string; uniqueId: string; value: string; @@ -53,6 +54,7 @@ export class PropertyBEModel { this.password = property.password; this.required = property.required; this.schema = property.schema; + this.schemaType = property.schemaType; this.type = property.type; this.uniqueId = property.uniqueId; this.value = property.value; diff --git a/catalog-ui/src/app/models/service-instance-properties.ts b/catalog-ui/src/app/models/service-instance-properties.ts new file mode 100644 index 0000000000..9e9f1cce7f --- /dev/null +++ b/catalog-ui/src/app/models/service-instance-properties.ts @@ -0,0 +1,31 @@ +/*! + * 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. + */ + +import {PropertyModel} from 'app/models'; + +export class ServiceInstanceObject { + id: string; + name: string; + properties: Array<PropertyModel> = []; + + constructor(input?:any) { + if(input) { + this.id = input.id; + this.name = input.name; + this.properties = input.properties; + } + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/modules/directive-module.ts b/catalog-ui/src/app/modules/directive-module.ts index 683a3344ca..c541d58208 100644 --- a/catalog-ui/src/app/modules/directive-module.ts +++ b/catalog-ui/src/app/modules/directive-module.ts @@ -183,6 +183,7 @@ 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 { ServiceDependenciesComponent } from '../ng2/components/logic/service-dependencies/service-dependencies.component'; import { MultilineEllipsisComponent } from "../ng2/shared/multiline-ellipsis/multiline-ellipsis.component"; import { InterfaceOperationComponent } from '../ng2/pages/interface-operation/interface-operation.page.component'; import { PluginFrameComponent } from "../ng2/components/ui/plugin/plugin-frame.component"; @@ -245,6 +246,12 @@ directiveModule.directive('ng2ServicePathSelector', downgradeComponent({ outputs: [] }) as angular.IDirectiveFactory); +directiveModule.directive('ng2ServiceDependencies', downgradeComponent({ + component: ServiceDependenciesComponent, + inputs: ['compositeService', 'currentServiceInstance', 'selectedInstanceProperties', 'selectedInstanceSiblings', 'selectedInstanceConstraints', 'readonly'], + outputs: ['updateRulesListEvent', 'loadRulesListEvent','dependencyStatus'] +}) as angular.IDirectiveFactory); + directiveModule.directive('interfaceOperation', downgradeComponent({ component: InterfaceOperationComponent, inputs: ['component', 'readonly'], diff --git a/catalog-ui/src/app/modules/view-model-module.ts b/catalog-ui/src/app/modules/view-model-module.ts index c1929e0e12..a390ab4fb4 100644 --- a/catalog-ui/src/app/modules/view-model-module.ts +++ b/catalog-ui/src/app/modules/view-model-module.ts @@ -24,6 +24,7 @@ import {WorkspaceViewModel} from "../view-models/workspace/workspace-view-model" import {CompositionViewModel} from "../view-models/workspace/tabs/composition/composition-view-model"; import {DetailsViewModel} from "../view-models/workspace/tabs/composition/tabs/details/details-view-model"; import {ResourceArtifactsViewModel} from "../view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model"; +import {ServiceDependenciesViewModel} from "../view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view-model"; import {PropertyFormBaseView} from "../view-models/forms/property-forms/base-property-form/property-form-base-model"; import {PropertyFormViewModel} from "../view-models/forms/property-forms/component-property-form/property-form-view-model"; import {ModulePropertyView} from "../view-models/forms/property-forms/module-property-modal/module-property-model"; @@ -82,6 +83,7 @@ viewModelModule .controller(moduleName + '.DetailsViewModel', DetailsViewModel) .controller(moduleName + '.ResourceArtifactsViewModel', ResourceArtifactsViewModel) + .controller(moduleName + '.ServiceDependenciesViewModel', ServiceDependenciesViewModel) .controller(moduleName + '.PropertyFormBaseView', PropertyFormBaseView) .controller(moduleName + '.PropertyFormViewModel', PropertyFormViewModel) .controller(moduleName + '.ModulePropertyView', ModulePropertyView) diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts index 750563b092..1ae2df2d82 100644 --- a/catalog-ui/src/app/ng2/app.module.ts +++ b/catalog-ui/src/app/ng2/app.module.ts @@ -60,6 +60,8 @@ import { ServicePathCreatorModule } from './pages/service-path-creator/service-p import { ServicePathsListModule } from './pages/service-paths-list/service-paths-list.module'; import { ServicePathModule } from 'app/ng2/components/logic/service-path/service-path.module'; import { ServicePathSelectorModule } from 'app/ng2/components/logic/service-path-selector/service-path-selector.module'; +import {ServiceDependenciesModule} from 'app/ng2/components/logic/service-dependencies/service-dependencies.module'; +import {ServiceDependenciesEditorModule} from './pages/service-dependencies-editor/service-dependencies-editor.module'; import { CompositionPanelModule } from 'app/ng2/pages/composition/panel/panel.module'; import { WindowRef } from "./services/window.service"; import {ArchiveService} from "./services/archive.service"; @@ -110,7 +112,9 @@ export function configServiceFactory(config: ConfigService) { ServicePathCreatorModule, ServicePathsListModule, ServicePathModule, - ServicePathSelectorModule + ServicePathSelectorModule, + ServiceDependenciesModule, + ServiceDependenciesEditorModule ], exports: [], entryComponents: [ diff --git a/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.html b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.html new file mode 100644 index 0000000000..d71a4fbecb --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.html @@ -0,0 +1,35 @@ +<div class="service-dependencies"> + <loader [display]="isLoading" [size]="'medium'" [relative]="true"></loader> + <div class="checkbox-label-mark-as-dependent"> + <checkbox + class="checkbox-label" + data-tests-id="checkbox-mark-as-dependent" + [label]="'Mark as Dependent'" + (checkedChange)="onMarkAsDependent()" + [(checked)]="isDependent" + [disabled]="readonly"> + </checkbox> + </div> + <div class="i-sdc-designer-sidebar-section-content-item-rules-section" *ngIf="isDependent"> + + <div class="i-sdc-designer-sidebar-section-content-item-rule" [ngClass]="{'hand': !readonly}" + *ngFor="let rule of rulesList; let i = index"> + <div class="rule-details" [ngClass]="{'readonly': readonly}"> + <div class="rule-desc" (click)="!readonly && onSelectRule(i)" tooltips tooltip="{{rule.servicePropertyName + ' ' + getSymbol(rule.constraintOperator) + ' ' + (rule.sourceName ? rule.sourceName + ':' : '') + rule.value}}"> + {{rule.servicePropertyName + ' ' + getSymbol(rule.constraintOperator) + ' ' + (rule.sourceName ? rule.sourceName + ':' : '') + rule.value}} + </div> + <span *ngIf="!readonly" class="sprite-new delete-btn delete-icon" (click)="openDeleteModal(i)" data-tests-id="delete-input-button"></span> + </div> + </div> + + <div class="w-sdc-designer-sidebar-section-footer" > + <button + class="w-sdc-designer-sidebar-section-footer-action add-rule-btn tlv-btn blue" + data-tests-id="add-rule-button" + (click)="onAddRule()" + [disabled]="readonly"> + {{'SERVICE_DEPENDENCY_ADD_RULE' | translate}} + </button> + </div> + </div> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.less b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.less new file mode 100644 index 0000000000..ae990dc85f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.less @@ -0,0 +1,70 @@ +@import './../../../../../assets/styles/variables.less'; +@import './../../../../../assets/styles/variables-old.less'; +@import './../../../../../assets/styles/mixins_old.less'; +@import './../../../../../assets/styles/mixins.less'; + +.service-dependencies { + + /deep/ .checkbox-label-mark-as-dependent { + padding: 7px 18px; + position: relative; + height: 61px; + color: @main_color_a; + box-shadow: 0 2px 7px @main_color_o; + border-bottom: 1px solid @main_color_o; + .checkbox-label { + margin-top: 14px; + .checkbox-label-content { + font-size: 14px; + } + } + .checkbox-container input[type=checkbox].checkbox-hidden[disabled] ~ .checkbox-label-content { + opacity: 0.5; + } + + loader { + top: 20px; + } + } + + .i-sdc-designer-sidebar-section-content-item-rules-section { + .i-sdc-designer-sidebar-section-content-item-rule { + border-bottom: 1px solid @main_color_o; + padding: 5px 10px 5px 18px; + position: relative; + height: 61px; + display: flex; + align-items: center; + justify-content: space-between; + + .rule-details { + .s_1; + display: flex; + flex: 1; + align-items: center; + justify-content: space-between; + margin-left: 5px; + width: 180px; + .delete-icon { + visibility: hidden; + } + &:not(.readonly):hover { + .a_1; + } + &:hover .delete-icon{ + visibility: visible; + } + &.readonly { + opacity: 0.5; + } + } + .rule-desc { + .sdc-ellipsis; + width: 220px; + position: relative; + padding-right: 1em; + } + + } + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.ts b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.ts new file mode 100644 index 0000000000..ac87576540 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.component.ts @@ -0,0 +1,325 @@ +/*! + * 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. + */ +import {Component, Input, Output, EventEmitter, ComponentRef} from '@angular/core'; +import {ModalService} from 'app/ng2/services/modal.service'; +import {Service, ComponentInstance, ModalModel, ButtonModel, PropertyBEModel, ServiceInstanceObject} from 'app/models'; +import {ServiceDependenciesEditorComponent} from 'app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component'; +import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component'; +import {ComponentServiceNg2} from 'app/ng2/services/component-services/component.service'; +import {TranslateService} from 'app/ng2/shared/translator/translate.service'; + +export class ConstraintObject { + servicePropertyName: string; + constraintOperator: string; + sourceType: string; + sourceName: string; + value: string; + + constructor(input?: any) { + if (input) { + this.servicePropertyName = input.servicePropertyName; + this.constraintOperator = input.constraintOperator; + this.sourceType = input.sourceType; + this.sourceName = input.sourceName; + this.value = input.value; + } + } +} + +export class ConstraintObjectUI extends ConstraintObject{ + isValidValue: boolean; + + constructor(input?: any) { + super(input); + if(input) { + this.isValidValue = input.isValidValue ? input.isValidValue : input.value !== ''; + } + } + + public updateValidity(isValidValue: boolean) { + this.isValidValue = isValidValue; + } + + public isValidRule(isStatic) { + let isValidValue = isStatic ? this.isValidValue : true; + return this.servicePropertyName != null && this.servicePropertyName !== '' + && this.value != null && this.value !== '' && isValidValue; + } +} + +export const OPERATOR_TYPES = { + EQUAL: 'equal', + GREATER_THAN: 'greater_than', + LESS_THAN: 'less_than' +}; + +class I18nTexts { + static uncheckModalTitle: string; + static uncheckModalText: string; + static modalApprove: string; + static modalCancel: string; + static modalCreate: string; + static modalSave: string; + static modalDelete: string; + static addRuleTxt: string; + static updateRuleTxt: string; + static deleteRuleTxt: string; + static deleteRuleMsg: string; + + public static translateTexts(translateService) { + I18nTexts.uncheckModalTitle = translateService.translate("SERVICE_DEPENDENCY_UNCHECK_TITLE"); + I18nTexts.uncheckModalText = translateService.translate("SERVICE_DEPENDENCY_UNCHECK_TEXT"); + I18nTexts.modalApprove = translateService.translate("MODAL_APPROVE"); + I18nTexts.modalCancel = translateService.translate("MODAL_CANCEL"); + I18nTexts.modalCreate = translateService.translate("MODAL_CREATE"); + I18nTexts.modalSave = translateService.translate("MODAL_SAVE"); + I18nTexts.modalDelete = translateService.translate("MODAL_DELETE"); + I18nTexts.addRuleTxt = translateService.translate("SERVICE_DEPENDENCY_ADD_RULE"); + I18nTexts.updateRuleTxt = translateService.translate("SERVICE_DEPENDENCY_UPDATE_RULE"); + I18nTexts.deleteRuleTxt = translateService.translate("SERVICE_DEPENDENCY_DELETE_RULE"); + I18nTexts.deleteRuleMsg = translateService.translate("SERVICE_DEPENDENCY_DELETE_RULE_MSG"); + } +} + + +@Component({ + selector: 'service-dependencies', + templateUrl: './service-dependencies.component.html', + styleUrls: ['service-dependencies.component.less'], + providers: [ModalService, TranslateService] +}) + +export class ServiceDependenciesComponent { + modalInstance: ComponentRef<ModalComponent>; + isDependent: boolean; + isLoading: boolean; + compositeServiceProperties: Array<PropertyBEModel> = []; + rulesList: Array<ConstraintObject> = []; + operatorTypes: Array<any>; + + @Input() readonly: boolean; + @Input() compositeService: Service; + @Input() currentServiceInstance: ComponentInstance; + @Input() selectedInstanceSiblings: Array<ServiceInstanceObject>; + @Input() selectedInstanceConstraints: Array<ConstraintObject> = []; + @Input() selectedInstanceProperties: Array<PropertyBEModel> = []; + @Output() updateRulesListEvent:EventEmitter<Array<ConstraintObject>> = new EventEmitter<Array<ConstraintObject>>(); + @Output() loadRulesListEvent:EventEmitter<any> = new EventEmitter(); + @Output() dependencyStatus = new EventEmitter<boolean>(); + + + constructor(private componentServiceNg2: ComponentServiceNg2, private ModalServiceNg2: ModalService, private translateService: TranslateService) { + } + + ngOnInit() { + this.isLoading = false; + this.operatorTypes = [ + {label: ">", value: OPERATOR_TYPES.GREATER_THAN}, + {label: "<", value: OPERATOR_TYPES.LESS_THAN}, + {label: "=", value: OPERATOR_TYPES.EQUAL} + ]; + this.componentServiceNg2.getServiceProperties(this.compositeService).subscribe((properties: Array<PropertyBEModel>) => { + this.compositeServiceProperties = properties; + }); + this.loadRules(); + this.translateService.languageChangedObservable.subscribe(lang => { + I18nTexts.translateTexts(this.translateService); + }); + } + + ngOnChanges(changes) { + if(changes.currentServiceInstance) { + this.currentServiceInstance = changes.currentServiceInstance.currentValue; + this.isDependent = this.currentServiceInstance.isDependent(); + } + if(changes.selectedInstanceConstraints && changes.selectedInstanceConstraints.currentValue !== changes.selectedInstanceConstraints.previousValue) { + this.selectedInstanceConstraints = changes.selectedInstanceConstraints.currentValue; + this.loadRules(); + } + } + + public openRemoveDependencyModal = (): ComponentRef<ModalComponent> => { + let actionButton: ButtonModel = new ButtonModel(I18nTexts.modalApprove, 'blue', this.onUncheckDependency); + let cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'grey', this.onCloseRemoveDependencyModal); + let modalModel: ModalModel = new ModalModel('sm', I18nTexts.uncheckModalTitle, I18nTexts.uncheckModalText, [actionButton, cancelButton]); + return this.ModalServiceNg2.createCustomModal(modalModel); + } + + loadRules() { + this.rulesList = this.selectedInstanceConstraints && this.selectedInstanceConstraints.map((co: ConstraintObject) => ({ + servicePropertyName: co.servicePropertyName, + constraintOperator: co.constraintOperator, + sourceType: co.sourceType, + sourceName: co.sourceName !== 'SELF' ? co.sourceName : this.compositeService.name, + value: co.value, + })); + } + + onUncheckDependency = () => { + this.ModalServiceNg2.closeCurrentModal(); + this.isLoading = true; + let isDepOrig = this.isDependent; + let rulesListOrig = this.rulesList; + this.currentServiceInstance.unmarkAsDependent(); + this.updateComponentInstance(isDepOrig, rulesListOrig); + } + + onCloseRemoveDependencyModal = () => { + this.isDependent = true; + this.ModalServiceNg2.closeCurrentModal(); + } + + onCheckDependency = () => { + let isDepOrig = this.isDependent; + let rulesListOrig = this.rulesList; + this.currentServiceInstance.markAsDependent(); + this.rulesList = []; + this.updateComponentInstance(isDepOrig, rulesListOrig); + } + + onMarkAsDependent() { + if(!this.currentServiceInstance.isDependent()) { + this.onCheckDependency(); + } + else { + this.openRemoveDependencyModal().instance.open(); + } + } + + updateComponentInstance(isDependent_origVal : boolean, rulesList_orig: Array<ConstraintObject>) { + this.isLoading = true; + this.componentServiceNg2.updateComponentInstance(this.compositeService, this.currentServiceInstance).subscribe((updatedServiceIns: ComponentInstance) => { + this.currentServiceInstance = new ComponentInstance(updatedServiceIns); + this.isDependent = this.currentServiceInstance.isDependent(); + this.dependencyStatus.emit(this.isDependent); + if(this.isDependent) { + this.loadRulesListEvent.emit(); + } + this.isLoading = false; + }, err=> { + this.isDependent = isDependent_origVal; + this.rulesList = rulesList_orig; + this.isLoading = false; + console.log('An error has occurred.'); + }); + } + + onAddRule () { + let cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.ModalServiceNg2.closeCurrentModal); + let saveButton: ButtonModel = new ButtonModel(I18nTexts.modalCreate, 'blue', this.createRule, this.getDisabled); + let modalModel: ModalModel = new ModalModel('l', I18nTexts.addRuleTxt, '', [saveButton, cancelButton], 'standard'); + this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); + this.ModalServiceNg2.addDynamicContentToModal( + this.modalInstance, + ServiceDependenciesEditorComponent, + { + currentServiceName: this.currentServiceInstance.name, + operatorTypes: this.operatorTypes, + compositeServiceName: this.compositeService.name, + compositeServiceProperties: this.compositeServiceProperties, + selectedInstanceProperties: this.selectedInstanceProperties, + selectedInstanceSiblings: this.selectedInstanceSiblings + } + ); + this.modalInstance.instance.open(); + } + + onSelectRule(index: number) { + let cancelButton: ButtonModel = new ButtonModel(I18nTexts.modalCancel, 'outline white', this.ModalServiceNg2.closeCurrentModal); + let saveButton: ButtonModel = new ButtonModel(I18nTexts.modalSave, 'blue', () => this.updateRules(), this.getDisabled); + let modalModel: ModalModel = new ModalModel('l', I18nTexts.updateRuleTxt, '', [saveButton, cancelButton], 'standard'); + this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); + this.ModalServiceNg2.addDynamicContentToModal( + this.modalInstance, + ServiceDependenciesEditorComponent, + { + serviceRuleIndex: index, + serviceRules: _.map(this.rulesList, rule => new ConstraintObjectUI(rule)), + currentServiceName: this.currentServiceInstance.name, + operatorTypes: this.operatorTypes, + compositeServiceName: this.compositeService.name, + compositeServiceProperties: this.compositeServiceProperties, + selectedInstanceProperties: this.selectedInstanceProperties, + selectedInstanceSiblings: this.selectedInstanceSiblings + } + ); + this.modalInstance.instance.open(); + } + + getDisabled = ():boolean => { + return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit(); + }; + + createRule = ():void => { + let newRuleToCreate: ConstraintObject = new ConstraintObject(this.modalInstance.instance.dynamicContent.instance.currentRule); + this.isLoading = true; + this.componentServiceNg2.createServiceFilterConstraints( + this.compositeService, + this.currentServiceInstance, + newRuleToCreate + ).subscribe( (response) => { + this.updateRulesListEvent.emit(response.properties); + this.isLoading = false; + }, err=> { + this.isLoading = false; + }); + this.ModalServiceNg2.closeCurrentModal(); + }; + + updateRules = ():void => { + let allRulesToUpdate: Array<ConstraintObject> = this.modalInstance.instance.dynamicContent.instance.serviceRulesList.map(rule => new ConstraintObject(rule)); + this.isLoading = true; + this.componentServiceNg2.updateServiceFilterConstraints( + this.compositeService, + this.currentServiceInstance, + allRulesToUpdate + ).subscribe((response) => { + this.updateRulesListEvent.emit(response.properties); + this.isLoading = false; + }, err => { + this.isLoading = false; + }); + this.ModalServiceNg2.closeCurrentModal(); + } + + getSymbol(constraintOperator) { + switch (constraintOperator) { + case OPERATOR_TYPES.LESS_THAN: return '<'; + case OPERATOR_TYPES.EQUAL: return '='; + case OPERATOR_TYPES.GREATER_THAN: return '>'; + } + } + + onDeleteRule = (index:number) => { + this.isLoading = true; + this.componentServiceNg2.deleteServiceFilterConstraints( + this.compositeService, + this.currentServiceInstance, + index + ).subscribe( (response) => { + this.updateRulesListEvent.emit(response.properties); + this.isLoading = false; + }, err=> { + this.isLoading = false; + }); + this.ModalServiceNg2.closeCurrentModal(); + }; + + openDeleteModal = (index:number) => { + this.ModalServiceNg2.createActionModal(I18nTexts.deleteRuleTxt, I18nTexts.deleteRuleMsg, + I18nTexts.modalDelete, () => this.onDeleteRule(index), I18nTexts.modalCancel).instance.open(); + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.module.ts b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.module.ts new file mode 100644 index 0000000000..7e66ed99c7 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-dependencies/service-dependencies.module.ts @@ -0,0 +1,24 @@ + +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServiceDependenciesComponent} from "./service-dependencies.component"; +import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; +import {TranslateModule} from 'app/ng2/shared/translator/translate.module'; + +@NgModule({ + declarations: [ + ServiceDependenciesComponent + ], + imports: [ + CommonModule, + UiElementsModule, + TranslateModule + ], + exports: [], + entryComponents: [ + ServiceDependenciesComponent + ], + providers: [] +}) +export class ServiceDependenciesModule { +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts b/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts index 76b0b9ec2b..4f283fb3f1 100644 --- a/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/dynamic-element/dynamic-element.component.ts @@ -175,8 +175,12 @@ export class DynamicElementComponent { tmp.push(new DropdownValue(true,'TRUE')); tmp.push(new DropdownValue(false,'FALSE')); this.cmpRef.instance.values = tmp; - if(!_.isUndefined(this.value)){//contains the real value (and not a string) - this.value = JSON.parse(this.value); + try { + if (typeof this.value === 'string') { + this.value = JSON.parse(this.value); + } + } catch(err) { + this.value = null; } break; diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/checkbox/checkbox.component.less b/catalog-ui/src/app/ng2/components/ui/form-components/checkbox/checkbox.component.less index 9df2680b6f..5c061a4b5e 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/checkbox/checkbox.component.less +++ b/catalog-ui/src/app/ng2/components/ui/form-components/checkbox/checkbox.component.less @@ -38,11 +38,16 @@ width:0; height:0; display:none; - &:checked ~ .checkbox-icon::before{ - .sprite-new; - .filled-checkbox-icon + &:checked { + ~ .checkbox-icon::before{ + .sprite-new; + .filled-checkbox-icon + } + &[disabled] ~ .checkbox-icon::before { + opacity: 0.5; + } } - &[disabled] ~ .checkbox-icon::before { + &[disabled]:not(:checked) ~ .checkbox-icon::before { /* TODO: add disabled styles here */ background-image: none; background-color: #EFEFEF; diff --git a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.html b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.html new file mode 100644 index 0000000000..f22e454332 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.html @@ -0,0 +1,56 @@ +<div class="service-dependencies-editor"> + <form class="w-sdc-form"> + + <div class="sdc-modal-top-bar"> + <div class="sdc-modal-top-bar-buttons" *ngIf="currentIndex >= 0"> + <span (click)="onChangePage(currentIndex - 1)" [ngClass]="{'disabled' : currentIndex === 0}" class="sprite-new left-arrow" data-tests-id="get-prev-rule" tooltip="Previous"></span> + <span (click)="onChangePage(currentIndex + 1)" [ngClass]="{'disabled' : currentIndex === input.serviceRules.length - 1}" class="sprite-new right-arrow" data-tests-id="get-next-rule" tooltip="Next"></span> + </div> + </div> + + <loader [display]="isLoading" [size]="'large'" [relative]="true"></loader> + + <div class="i-sdc-form-content"> + <div class="rule-builder-content"> + <div class="i-sdc-form-item rule-input-field"> + <label class="i-sdc-form-label required">Service {{currentServiceName}} Property</label> + <ui-element-dropdown + class="i-sdc-form-select" + data-tests-id="servicePropertyName" + [values]="ddValueSelectedServicePropertiesNames" + [(value)]="currentRule.servicePropertyName" + (change)="onServicePropertyChanged()"> + </ui-element-dropdown> + </div> + + <div class="i-sdc-form-item rule-input-field operator"> + <ui-element-dropdown class="i-sdc-form-select" data-tests-id="constraintOperator" [values]="operatorTypes" [(value)]="currentRule.constraintOperator"></ui-element-dropdown> + </div> + + <div class="i-sdc-form-item rule-input-field"> + <label class="i-sdc-form-label required" >Source</label> + <ui-element-dropdown class="i-sdc-form-select" data-tests-id="sourceType" [values]="sourceTypes" [(value)]="currentRule.sourceName" (change)="onSelectSourceType($event)"></ui-element-dropdown> + </div> + + <div class="rule-input-field assigned-value-field"> + <label class="i-sdc-form-label required" >{{assignedValueLabel}}</label> + <dynamic-element + *ngIf="currentRule.sourceType === SOURCE_TYPES.STATIC.value" + [(value)]="currentRule.value" + class="rule-assigned-value" + data-tests-id="ruleAssignedValue" + (elementChanged)="onValueChange($event.isValid)" + [type]="selectedPropertyObj ? selectedPropertyObj.type : 'string'"> + </dynamic-element> + <ui-element-dropdown *ngIf="currentRule.sourceType !== SOURCE_TYPES.STATIC.value" + class="rule-assigned-value" + data-tests-id="ruleAssignedValue" + [(value)]="currentRule.value" + [values]="listOfValuesToAssign"> + </ui-element-dropdown> + </div> + </div> + </div> + </form> + +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.less b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.less new file mode 100644 index 0000000000..e03b73c8c0 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.less @@ -0,0 +1,43 @@ +@import './../../../../assets/styles/variables.less'; + +.sdc-modal-top-bar { + display: flex; + justify-content: flex-end; +} + +.i-sdc-form-content { + display: flex; + flex-direction: column; + margin-top: 10px; + padding-bottom: 20px; + + .i-sdc-form-item { + width: 250px; + &.operation { + width: 60px; + } + } + + .rule-builder-content { + display: flex; + align-items: flex-end; + .rule-input-field { + flex: 1; + &:not(:last-of-type) { + margin-right: 20px; + } + &.operator{ + width: 55px; + flex: 0 1 auto; + } + &.assigned-value-field { + margin-bottom: 10px; + } + /deep/ ui-element-dropdown select, + /deep/ ui-element-input input { + height: 30px; + } + } + + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts new file mode 100644 index 0000000000..db5e7a9a31 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.component.ts @@ -0,0 +1,192 @@ +/*! + * 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. + */ +import { Component } from '@angular/core'; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {ConstraintObjectUI, OPERATOR_TYPES} from 'app/ng2/components/logic/service-dependencies/service-dependencies.component'; +import {ServiceInstanceObject, PropertyBEModel} from 'app/models'; +import { PROPERTY_DATA } from 'app/utils'; +import {DropdownValue} from 'app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component'; + +export class UIDropDownSourceTypesElement extends DropdownValue{ + options: Array<any>; + assignedLabel: string; + type: string; + constructor(input?: any){ + if(input) { + let value = input.value || ''; + let label = input.label || ''; + super(value, label); + this.options = input.options; + this.assignedLabel = input.assignedLabel; + this.type = input.type; + } + } +} + +@Component({ + selector: 'service-dependencies-editor', + templateUrl: './service-dependencies-editor.component.html', + styleUrls:['./service-dependencies-editor.component.less'], + providers: [ServiceServiceNg2] +}) + +export class ServiceDependenciesEditorComponent { + + input: { + serviceRuleIndex: number, + serviceRules: Array<ConstraintObjectUI>, + compositeServiceName: string, + currentServiceName: string, + compositeServiceProperties: Array<PropertyBEModel>, + selectedInstanceProperties: Array<PropertyBEModel>, + operatorTypes: Array<DropdownValue>, + selectedInstanceSiblings: Array<ServiceInstanceObject> + }; + currentServiceName: string; + selectedServiceProperties: Array<PropertyBEModel>; + selectedPropertyObj: PropertyBEModel; + ddValueSelectedServicePropertiesNames: Array<DropdownValue>; + operatorTypes: Array<DropdownValue>; + sourceTypes: Array<UIDropDownSourceTypesElement> = []; + currentRule: ConstraintObjectUI; + currentIndex: number; + listOfValuesToAssign: Array<DropdownValue>; + listOfSourceOptions: Array<PropertyBEModel>; + assignedValueLabel: string; + serviceRulesList: Array<ConstraintObjectUI>; + + + SOURCE_TYPES = { + STATIC: {label: 'Static', value: 'static'}, + SERVICE_PROPERTY: {label: 'Service Property', value: 'property'} + }; + + + ngOnInit() { + this.currentIndex = this.input.serviceRuleIndex; + this.serviceRulesList = this.input.serviceRules; + this.currentRule = this.serviceRulesList && this.input.serviceRuleIndex >= 0 ? + this.serviceRulesList[this.input.serviceRuleIndex]: + new ConstraintObjectUI({sourceName: this.SOURCE_TYPES.STATIC.value, sourceType: this.SOURCE_TYPES.STATIC.value, value: "", constraintOperator: OPERATOR_TYPES.EQUAL}); + this.currentServiceName = this.input.currentServiceName; + this.operatorTypes = this.input.operatorTypes; + this.selectedServiceProperties = this.input.selectedInstanceProperties; + this.ddValueSelectedServicePropertiesNames = _.map(this.input.selectedInstanceProperties, prop => new DropdownValue(prop.name, prop.name)); + this.initSourceTypes(); + this.syncRuleData(); + this.updateSourceTypesRelatedValues(); + } + + initSourceTypes() { + this.sourceTypes.push({label: this.SOURCE_TYPES.STATIC.label, value: this.SOURCE_TYPES.STATIC.value, + options: [], assignedLabel: this.SOURCE_TYPES.STATIC.label, type: this.SOURCE_TYPES.STATIC.value}); + this.sourceTypes.push({ + label: this.input.compositeServiceName, + value: this.input.compositeServiceName, + assignedLabel: this.SOURCE_TYPES.SERVICE_PROPERTY.label, + type: this.SOURCE_TYPES.SERVICE_PROPERTY.value, + options: this.input.compositeServiceProperties + }); + _.forEach(this.input.selectedInstanceSiblings, sib => + this.sourceTypes.push({ + label: sib.name, + value: sib.name, + options: sib.properties || [], + assignedLabel: this.SOURCE_TYPES.SERVICE_PROPERTY.label, + type: this.SOURCE_TYPES.SERVICE_PROPERTY.value + }) + ); + } + + syncRuleData() { + if(!this.currentRule.sourceName && this.currentRule.sourceType === this.SOURCE_TYPES.STATIC.value) { + this.currentRule.sourceName = this.SOURCE_TYPES.STATIC.value; + } + this.selectedPropertyObj = _.find(this.selectedServiceProperties, prop => prop.name === this.currentRule.servicePropertyName); + this.updateOperatorTypesList(); + this.updateSourceTypesRelatedValues(); + } + + updateOperatorTypesList() { + if (this.selectedPropertyObj && PROPERTY_DATA.SIMPLE_TYPES_COMPARABLE.indexOf(this.selectedPropertyObj.type) === -1) { + this.operatorTypes = [{label: "=", value: OPERATOR_TYPES.EQUAL}]; + this.currentRule.constraintOperator = OPERATOR_TYPES.EQUAL; + } + else { + this.operatorTypes = this.input.operatorTypes; + } + } + + updateSourceTypesRelatedValues() { + if(this.currentRule.sourceName) { + let selectedSourceType: UIDropDownSourceTypesElement = this.sourceTypes.find( + t => t.value === this.currentRule.sourceName && t.type === this.currentRule.sourceType + ); + this.listOfSourceOptions = selectedSourceType.options || []; + this.assignedValueLabel = selectedSourceType.assignedLabel || this.SOURCE_TYPES.STATIC.label; + this.filterOptionsByType(); + } + } + + onChangePage(newIndex) { + if (newIndex >= 0 && newIndex < this.input.serviceRules.length) { + this.currentIndex = newIndex; + this.currentRule = this.serviceRulesList[newIndex]; + this.syncRuleData(); + } + } + + onServicePropertyChanged() { + this.selectedPropertyObj = _.find(this.selectedServiceProperties, prop => prop.name === this.currentRule.servicePropertyName); + this.updateOperatorTypesList(); + this.filterOptionsByType(); + this.currentRule.value = ''; + } + + onSelectSourceType() { + this.currentRule.sourceType = this.currentRule.sourceName === this.SOURCE_TYPES.STATIC.value ? + this.SOURCE_TYPES.STATIC.value : + this.SOURCE_TYPES.SERVICE_PROPERTY.value; + this.updateSourceTypesRelatedValues(); + this.currentRule.value = ''; + } + + filterOptionsByType() { + if(!this.selectedPropertyObj) { + this.listOfValuesToAssign = []; + return; + } + this.listOfValuesToAssign = this.listOfSourceOptions.reduce((result, op:PropertyBEModel) => { + if(op.type === this.selectedPropertyObj.type && op.schemaType === this.selectedPropertyObj.schemaType) { + result.push(new DropdownValue(op.name, op.name)); + } + return result; + }, []); + } + + onValueChange(isValidValue) { + this.currentRule.updateValidity(isValidValue); + } + + checkFormValidForSubmit() { + if(!this.serviceRulesList) { //for create modal + let isStatic = this.currentRule.sourceName === this.SOURCE_TYPES.STATIC.value; + return this.currentRule.isValidRule(isStatic); + } + //for update all rules + return this.serviceRulesList.every(rule => rule.isValidRule(rule.sourceName === this.SOURCE_TYPES.STATIC.value)); + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts new file mode 100644 index 0000000000..98ac997bf7 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-dependencies-editor/service-dependencies-editor.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServiceDependenciesEditorComponent} from "./service-dependencies-editor.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"; + +@NgModule({ + declarations: [ + ServiceDependenciesEditorComponent + ], + imports: [ + CommonModule, + FormsModule, + FormElementsModule, + UiElementsModule + ], + exports: [], + entryComponents: [ + ServiceDependenciesEditorComponent + ], + providers: [] +}) +export class ServiceDependenciesEditorModule { +}
\ No newline at end of file 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 69871abe04..e9036a805d 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,15 +24,17 @@ 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, PropertyBEModel, OperationModel, BEOperationModel, CreateOperationResponse} from "app/models"; +import { Component, ComponentInstance, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData, + PropertyBEModel, OperationModel, BEOperationModel, CreateOperationResponse} from "app/models"; import {downgradeInjectable} from '@angular/upgrade/static'; -import {COMPONENT_FIELDS, CommonUtils} from "app/utils"; +import {COMPONENT_FIELDS, CommonUtils, SERVICE_FIELDS} 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"; import { ComponentType, ServerTypeUrl } from "../../../utils/constants"; import { HttpService } from '../http.service'; import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; +import {ConstraintObject} from 'app/ng2/components/logic/service-dependencies/service-dependencies.component'; import {IDependenciesServerResponse} from "../responses/dependencies-server-response"; import {AutomatedUpgradeGenericResponse} from "../responses/automated-upgrade-response"; import {IAutomatedUpgradeRequestObj} from "../../pages/automated-upgrade/automated-upgrade.service"; @@ -82,6 +84,10 @@ export class ComponentServiceNg2 { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES, COMPONENT_FIELDS.COMPONENT_INSTANCES_ATTRIBUTES]); } + getComponentInstanceProperties(component:Component):Observable<ComponentGenericResponse> { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_PROPERTIES]); + } + getComponentAttributes(component:Component):Observable<ComponentGenericResponse> { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_ATTRIBUTES]); } @@ -226,7 +232,7 @@ export class ComponentServiceNg2 { return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input') .map((res:Response) => { return new InputBEModel(res.json()); - }) + }); } updateComponentInputs(component:Component, inputs:InputBEModel[]):Observable<InputBEModel[]> { @@ -297,5 +303,37 @@ export class ComponentServiceNg2 { return res.json(); }); } + + updateComponentInstance(component:Component, componentInstance:ComponentInstance):Observable<ComponentInstance> { + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstance.uniqueId, componentInstance) + .map((res:Response) => { + return res.json(); + }); + } + + getServiceFilterConstraints(component:Component):Observable<ComponentGenericResponse> { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [SERVICE_FIELDS.NODE_FILTER]); + } + + createServiceFilterConstraints(component:Component, componentInstance:ComponentInstance, constraint:ConstraintObject):Observable<any> { + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter', constraint) + .map((res:Response) => { + return res.json(); + }); + } + + updateServiceFilterConstraints(component:Component, componentInstance:ComponentInstance, constraints:Array<ConstraintObject>):Observable<any> { + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter/', constraints) + .map((res:Response) => { + return res.json(); + }); + } + + deleteServiceFilterConstraints(component:Component, componentInstance:ComponentInstance, constraintIndex:number) { + return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstances/' + componentInstance.uniqueId + '/nodeFilter/' + constraintIndex) + .map((res:Response) => { + return res.json(); + }); + } } 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 647cc927f7..37ccf381c9 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 @@ -52,6 +52,7 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR public interfaceOperations:Array<OperationModel>; public additionalInformation:any; public derivedList:Array<any>; + public nodeFilterData: Array<any>; deserialize (response): ComponentGenericResponse { @@ -105,6 +106,9 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR if(response.policies) { this.policies = CommonUtils.initPolicies(response.policies); } + if(response.nodeFilterData) { + this.nodeFilterData = response.nodeFilterData; + } return this; } } diff --git a/catalog-ui/src/app/utils/constants.ts b/catalog-ui/src/app/utils/constants.ts index 0694560549..379f2f17c5 100644 --- a/catalog-ui/src/app/utils/constants.ts +++ b/catalog-ui/src/app/utils/constants.ts @@ -124,6 +124,7 @@ export class SOURCES { export class PROPERTY_DATA { public static TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME, PROPERTY_TYPES.LIST, PROPERTY_TYPES.MAP]; public static SIMPLE_TYPES = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT, PROPERTY_TYPES.BOOLEAN, PROPERTY_TYPES.JSON, PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME]; + public static SIMPLE_TYPES_COMPARABLE = [PROPERTY_TYPES.STRING, PROPERTY_TYPES.INTEGER, PROPERTY_TYPES.FLOAT]; public static SCALAR_TYPES = [PROPERTY_TYPES.SCALAR, PROPERTY_TYPES.SCALAR_FREQUENCY, PROPERTY_TYPES.SCALAR_SIZE, PROPERTY_TYPES.SCALAR_TIME]; public static ROOT_DATA_TYPE = "tosca.datatypes.Root"; public static OPENECOMP_ROOT = "org.openecomp.datatypes.Root"; @@ -327,6 +328,10 @@ export class GRAPH_EVENTS { static ON_GROUP_INSTANCE_UPDATE = 'onGroupInstanceUpdate'; } +export class DEPENDENCY_EVENTS { + static ON_DEPENDENCY_CHANGE = 'onDependencyStatusChange'; +} + export class COMPONENT_FIELDS { static COMPONENT_INSTANCES_PROPERTIES = "componentInstancesProperties"; @@ -351,6 +356,7 @@ export class COMPONENT_FIELDS { export class SERVICE_FIELDS { static FORWARDING_PATHS = "forwardingPaths"; + static NODE_FILTER = "nodeFilter"; } export class API_QUERY_PARAMS { diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts index a77377bac4..2270a7b06e 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view-model.ts @@ -71,6 +71,11 @@ export interface ICompositionViewModelScope extends IWorkspaceViewModelScope { isPNF():boolean; isConfiguration():boolean; preventMoveTab(state: boolean):void; + registerCreateInstanceEvent(callback: Function):void; + unregisterCreateInstanceEvent():void; + registerChangeComponentInstanceNameEvent(callback: Function):void; + unregisterChangeComponentInstanceNameEvent():void; + ComponentServiceNg2:ComponentServiceNg2, cacheComponentsInstancesFullData:Component; } @@ -116,7 +121,7 @@ export class CompositionViewModel { private ComponentServiceFactoryNg2: ComponentServiceFactoryNg2, private ComponentServiceNg2:ComponentServiceNg2, private Notification:any - ) { + ) { this.$scope.setValidState(true); this.initScope(); @@ -165,12 +170,12 @@ export class CompositionViewModel { this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance); this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, scope.setSelectedZoneInstance); this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick); - this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_START, () => { + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_START, () => { scope.isCanvasTagging = true; this.eventListenerService.notifyObservers(EVENTS.ON_WORKSPACE_UNSAVED_CHANGES, true, this.showUnsavedChangesAlert); }); - this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_END, () => { - scope.isCanvasTagging = false; + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CANVAS_TAG_END, () => { + scope.isCanvasTagging = false; this.resetUnsavedChanges(); }); this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_INSTANCE_NAME_CHANGED, scope.changeZoneInstanceName); @@ -184,13 +189,13 @@ export class CompositionViewModel { title: "Unsaved Changes", size: 'sm', type: 'custom', - + buttons: [ {id: 'cancelButton', text: 'Cancel', type: 'secondary', size: 'xsm', closeModal: true, callback: () => reject()}, {id: 'discardButton', text: 'Discard', type: 'secondary', size: 'xsm', closeModal: true, callback: () => { this.resetUnsavedChanges(); resolve()}}, {id: 'saveButton', text: 'Save', type: 'primary', size: 'xsm', closeModal: true, callback: () => { reject(); this.saveUnsavedChanges(afterSave); }} - ] as IModalButtonComponent[] - }, UnsavedChangesComponent, { isValidChangedData: true}); + ] as IModalButtonComponent[] + }, UnsavedChangesComponent, { isValidChangedData: true}); }); return deferred; @@ -225,7 +230,7 @@ export class CompositionViewModel { testId: "renameInstanceModal", buttons: [ {id: 'saveButton', text: 'OK', size: 'xsm', callback: this.saveInstanceName, closeModal: false}, - {id: 'cancelButton', text: 'Cancel', size: 'sm', closeModal: true} + {id: 'cancelButton', text: 'Cancel', size: 'sm', closeModal: true} ] }; @@ -274,7 +279,7 @@ export class CompositionViewModel { } else { this.ModalServiceSdcUI.closeModal(); } - + }; private removeSelectedComponentInstance = ():void => { @@ -372,12 +377,12 @@ export class CompositionViewModel { this.$scope.selectedZoneInstance = null; this.$scope.updateSelectedComponent(); - - - if (this.$state.current.name === 'workspace.composition.api') { this.$state.go('workspace.composition.details'); } + if(!selectedComponent.isServiceProxy() && (this.$state.current.name === 'workspace.composition.consumption' || this.$state.current.name === 'workspace.composition.dependencies')) { + this.$state.go('workspace.composition.details'); + } }; this.$scope.setSelectedZoneInstance = (zoneInstance: ZoneInstance): void => { @@ -390,7 +395,7 @@ export class CompositionViewModel { this.$scope.selectedZoneInstance = null; this.$scope.selectedComponent = this.$scope.currentComponent; - if (this.$state.current.name === 'workspace.composition.api') { + if (this.$state.current.name === 'workspace.composition.api' || this.$state.current.name === 'workspace.composition.consumption' || this.$state.current.name === 'workspace.composition.dependencies') { this.$state.go('workspace.composition.details'); } @@ -406,7 +411,7 @@ export class CompositionViewModel { this.$scope.changeZoneInstanceName = (newName:string):void => { this.$scope.selectedZoneInstance.instanceData.name = newName; }; - + this.$scope.deleteSelectedComponentInstance = ():void => { const {currentComponent} = this.$scope; const {title, message} = this.$scope.sdcMenu.alertMessages['deleteInstance']; @@ -477,5 +482,20 @@ export class CompositionViewModel { this.eventListenerService.registerObserverCallback(EVENTS.ON_LIFECYCLE_CHANGE, this.$scope.reload); + this.$scope.registerCreateInstanceEvent = (callback: Function): void => { + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_CREATE_COMPONENT_INSTANCE, callback); + }; + + this.$scope.unregisterCreateInstanceEvent = (): void => { + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_CREATE_COMPONENT_INSTANCE); + }; + + this.$scope.registerChangeComponentInstanceNameEvent = (callback: Function): void => { + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, callback); + }; + + this.$scope.unregisterChangeComponentInstanceNameEvent = (): void => { + this.eventListenerService.unRegisterObserver(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED); + }; } } diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html index 75265d9137..a571fdca0d 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition-view.html @@ -103,7 +103,7 @@ </button> <button data-ng-if="!selectedComponent.isService() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" ui-sref="workspace.composition.relations" - tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" + tooltips tooltip-class="tooltip-custom tab-tooltip {{currentComponent.selectedInstance.isServiceProxy() ? '' : 'tooltip-rightside'}}" data-tests-id="requirements-and-capabilities" tooltip-content="Requirements and Capabilities" data-ng-class="{'disabled': disabledTabs}"> @@ -115,6 +115,14 @@ data-ng-class="{'disabled': disabledTabs}"> <div class="i-sdc-designer-sidebar-tab-icon sprite-new api"></div> </button> + <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" + data-ui-sref="workspace.composition.dependencies" + tooltips tooltip-class="tooltip-custom tab-tooltip " tooltip-content="Service Dependencies" + data-tests-id="service-dependency-tab" + data-ng-if="(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" + data-ng-class="{'disabled': disabledTabs}"> + <div class="i-sdc-designer-sidebar-tab-icon sprite-new dependencies-icon"></div> + </button> </div> <div data-ui-view="" class="w-sdc-designer-sidebar-tab-content-view"></div> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts index e389395142..36ceabfb42 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view-model.ts @@ -20,7 +20,7 @@ 'use strict'; import * as _ from "lodash"; -import {Component} from "app/models"; +import {Component, ModalModel, ButtonModel} from "app/models"; import {GRAPH_EVENTS} from "app/utils"; import {LeftPaletteLoaderService, EventListenerService} from "app/services"; import {ICompositionViewModelScope} from "../../composition-view-model"; @@ -28,6 +28,7 @@ import {LeftPaletteComponent} from "../../../../../../models/components/displayC import {ComponentServiceFactoryNg2} from "app/ng2/services/component-services/component.service.factory"; import {ServiceServiceNg2} from 'app/ng2/services/component-services/service.service'; import {Service} from "app/models/components/service"; +import {ModalService} from 'app/ng2/services/modal.service'; export interface IEditResourceVersion { allVersions:any; @@ -41,24 +42,31 @@ interface IDetailsViewModelScope extends ICompositionViewModelScope { editForm:ng.IFormController; editResourceVersion:IEditResourceVersion; - changeResourceVersion():void; + onChangeResourceVersion():void; + alertBeforeChangeResourceVersion():void; + changeVersion():void; + cancelChangeResourceVersion():void; } export class DetailsViewModel { static '$inject' = [ '$scope', + '$filter', 'LeftPaletteLoaderService', 'EventListenerService', 'ComponentServiceFactoryNg2', - 'ServiceServiceNg2' + 'ServiceServiceNg2', + 'ModalServiceNg2' ]; constructor(private $scope:IDetailsViewModelScope, + private $filter:ng.IFilterService, private LeftPaletteLoaderService:LeftPaletteLoaderService, private eventListenerService:EventListenerService, private ComponentServiceFactoryNg2: ComponentServiceFactoryNg2, - private serviceService: ServiceServiceNg2) { + private serviceService: ServiceServiceNg2, + private ModalServiceNg2: ModalService) { this.initScope(); } @@ -115,7 +123,35 @@ export class DetailsViewModel { } }); - this.$scope.changeResourceVersion = ():void => { + this.$scope.onChangeResourceVersion = ():void => { + if(this.$scope.isComponentInstanceSelected() && this.$scope.currentComponent.selectedInstance.isServiceProxy()) { + this.$scope.alertBeforeChangeResourceVersion(); + } + else { + this.$scope.changeVersion(); + } + }; + + this.$scope.alertBeforeChangeResourceVersion = ():void => { + let modalApproveTxt:string = this.$filter('translate')("MODAL_APPROVE"); + let modalCancelTxt:string = this.$filter('translate')("MODAL_CANCEL"); + let changeVersionModalTitle:string = this.$filter('translate')("DETAILS_TAB_CHANGE_VERSION_MODAL_TITLE"); + let changeVersionModalMsg:string = this.$filter('translate')("DETAILS_TAB_CHANGE_VERSION_MODAL_MSG"); + + let actionButton: ButtonModel = new ButtonModel(modalApproveTxt, 'blue', this.$scope.changeVersion); + let cancelButton: ButtonModel = new ButtonModel(modalCancelTxt, 'grey', this.$scope.cancelChangeResourceVersion); + let modalModel: ModalModel = new ModalModel('sm', changeVersionModalTitle, changeVersionModalMsg, [actionButton, cancelButton]); + let customModal = this.ModalServiceNg2.createCustomModal(modalModel); + customModal.instance.open(); + }; + + this.$scope.cancelChangeResourceVersion = () => { + this.ModalServiceNg2.closeCurrentModal(); + this.$scope.editResourceVersion.changeVersion = this.$scope.currentComponent.selectedInstance.componentVersion; + }; + + this.$scope.changeVersion = ():void => { + this.ModalServiceNg2.closeCurrentModal(); this.$scope.isLoading = true; this.$scope.$parent.isLoading = true; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html index 145943e9fd..db5322a859 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/details/details-view.html @@ -46,7 +46,7 @@ data-ng-disabled="$parent.isViewOnly || selectedComponent.uniqueId != editResourceVersion.allVersions[editResourceVersion.changeVersion] || selectedComponent.archived" class="i-sdc-designer-sidebar-section-content-item-value i-sdc-form-select" data-ng-class="{'minor': (editResourceVersion.changeVersion)%1, 'disabled':selectedComponent.archived}" - data-ng-change="changeResourceVersion()"> + data-ng-change="onChangeResourceVersion()"> <option class="select-instance-version" data-ng-class="{'minor': key%1}" ng-repeat="(key, value) in editResourceVersion.allVersions">{{key}}</option> </select></ng-form> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view-model.ts new file mode 100644 index 0000000000..b634e6021f --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view-model.ts @@ -0,0 +1,125 @@ +/*! + * 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. + */ + + +import {ICompositionViewModelScope} from "../../composition-view-model"; +import {Service, ComponentInstance, PropertiesGroup, ServiceInstanceObject, PropertyBEModel} from 'app/models'; +import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service"; +import {ConstraintObject} from "app/ng2/components/logic/service-dependencies/service-dependencies.component"; +import {ComponentGenericResponse} from 'app/ng2/services/responses/component-generic-response'; +import {DEPENDENCY_EVENTS} from "app/utils/constants"; +import {EventListenerService} from 'app/services'; + +interface IServiceDependenciesViewModelScope extends ICompositionViewModelScope { + service: Service; + selectedInstanceSiblings: Array<ServiceInstanceObject>; + componentInstancesConstraints: Array<any>; + selectedInstanceConstraints: Array<ConstraintObject>; + selectedInstanceProperties: Array<PropertyBEModel>; + updateSelectedInstanceConstraints(constraintsList:Array<ConstraintObject>): void; + loadConstraints(): void; + componentInstanceProperties: PropertiesGroup; + notifyDependencyEventsObserver: Function; +} + + + +export class ServiceDependenciesViewModel { + + static '$inject' = [ + '$scope', + 'ComponentServiceNg2', + 'EventListenerService' + ]; + + constructor(private $scope:IServiceDependenciesViewModelScope, private ComponentServiceNg2:ComponentServiceNg2, private eventListenerService: EventListenerService) { + this.$scope.service = <Service>this.$scope.currentComponent; + this.$scope.notifyDependencyEventsObserver = this.notifyDependencyEventsObserver; + this.initInstancesWithProperties(); + this.loadConstraints(); + + this.initScope(); + } + + private initInstancesWithProperties = ():void => { + this.ComponentServiceNg2.getComponentInstanceProperties(this.$scope.currentComponent).subscribe((genericResponse:ComponentGenericResponse) => { + this.$scope.componentInstanceProperties = genericResponse.componentInstancesProperties; + this.updateInstanceAttributes(); + }); + } + + private updateInstanceAttributes = ():void => { + if (this.$scope.isComponentInstanceSelected() && this.$scope.componentInstanceProperties) { + let instancesMappedList = this.$scope.service.componentInstances.map(coInstance => new ServiceInstanceObject({ + id: coInstance.uniqueId, + name: coInstance.name, + properties: this.$scope.componentInstanceProperties[coInstance.uniqueId] || [] + })); + this.$scope.selectedInstanceProperties = this.$scope.componentInstanceProperties[this.$scope.currentComponent.selectedInstance.uniqueId]; + this.$scope.selectedInstanceSiblings = instancesMappedList.filter(coInstance => coInstance.id !== this.$scope.currentComponent.selectedInstance.uniqueId); + } + } + + private initScope = ():void => { + this.$scope.$watch('currentComponent.selectedInstance', (newInstance:ComponentInstance):void => { + if (angular.isDefined(newInstance) && this.$scope.componentInstancesConstraints) { + this.updateInstanceAttributes(); + this.$scope.selectedInstanceConstraints = this.$scope.componentInstancesConstraints[this.$scope.currentComponent.selectedInstance.uniqueId] ? + this.$scope.componentInstancesConstraints[this.$scope.currentComponent.selectedInstance.uniqueId].properties : + []; + } + }); + this.$scope.$watch('componentInstancesConstraints', (constraints: Array<any>):void => { + if (angular.isDefined(constraints)) { + if(this.$scope.isComponentInstanceSelected()) { + this.$scope.selectedInstanceConstraints = this.$scope.componentInstancesConstraints[this.$scope.currentComponent.selectedInstance.uniqueId] ? + this.$scope.componentInstancesConstraints[this.$scope.currentComponent.selectedInstance.uniqueId].properties || [] : + []; + } + } + }); + + this.$scope.updateSelectedInstanceConstraints = (constraintsList:Array<ConstraintObject>):void => { + this.$scope.componentInstancesConstraints[this.$scope.currentComponent.selectedInstance.uniqueId].properties = constraintsList; + this.$scope.selectedInstanceConstraints = this.$scope.componentInstancesConstraints[this.$scope.currentComponent.selectedInstance.uniqueId].properties; + } + + this.$scope.loadConstraints = ():void => { + this.loadConstraints(); + } + + this.$scope.registerCreateInstanceEvent(() => { + this.initInstancesWithProperties(); + }); + + this.$scope.registerChangeComponentInstanceNameEvent((updatedComponentInstance) => { + this.$scope.currentComponent.selectedInstance = updatedComponentInstance; + }); + + this.$scope.$on('$destroy', this.$scope.unregisterCreateInstanceEvent); + this.$scope.$on('$destroy', this.$scope.unregisterChangeComponentInstanceNameEvent); + } + + private loadConstraints = ():void => { + this.ComponentServiceNg2.getServiceFilterConstraints(this.$scope.service).subscribe((response) => { + this.$scope.componentInstancesConstraints = response.nodeFilterData; + }); + } + + public notifyDependencyEventsObserver = (isChecked: boolean):void => { + this.eventListenerService.notifyObservers(DEPENDENCY_EVENTS.ON_DEPENDENCY_CHANGE, isChecked); + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view.html b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view.html new file mode 100644 index 0000000000..ba50994529 --- /dev/null +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/service-dependencies/service-dependencies-view.html @@ -0,0 +1,25 @@ + +<perfect-scrollbar class="w-sdc-designer-sidebar-tab-content service-dependencies"> + <div class="w-sdc-designer-sidebar-section"> + <expand-collapse expanded-selector=".w-sdc-designer-sidebar-section-content" + class="w-sdc-designer-sidebar-section-title"> + <span class="w-sdc-designer-sidebar-section-title-text" tooltips tooltip-content="Service Dependencies">Service Dependencies</span> + <div class="w-sdc-designer-sidebar-section-title-icon"></div> + </expand-collapse> + <div class="w-sdc-designer-sidebar-section-content" data-ng-if="isComponentInstanceSelected()"> + <div class="i-sdc-designer-sidebar-section-content-item"> + <ng2-service-dependencies + [composite-service]="service" + [current-service-instance]="currentComponent.selectedInstance" + [selected-instance-properties]="selectedInstanceProperties" + [selected-instance-siblings]="selectedInstanceSiblings" + [selected-instance-constraints]="selectedInstanceConstraints" + [readonly]="isViewMode() || !isDesigner()" + (dependency-status)="notifyDependencyEventsObserver($event)" + (update-rules-list-event)="updateSelectedInstanceConstraints($event)" + (load-rules-list-event)="loadConstraints()"> + </ng2-service-dependencies> + </div> + </div> + </div> +</perfect-scrollbar>
\ No newline at end of file diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index 8e43f18bce..1c68091a28 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -42,7 +42,6 @@ "GENERAL_LABEL_SYSTEM_NAME": "System Name:", "GENERAL_LABEL_SOURCE_SERVICE_NAME": "Source Service Name:", "GENERAL_LABEL_RESOURCE_CUSTOMIZATION_UUID": "Resource Customization UUID:", - "=========== GENERAL_TAB ===========": "", "GENERAL_TAB_LABEL_RESOURCE_MODEL_NUMBER":"Vendor Model Number", @@ -390,7 +389,7 @@ "DEPLOYMENT_ARTIFACT_BUTTON_ADD_VOLUME_HEAT": "Add Volume HEAT Artifact", "DEPLOYMENT_ARTIFACT_BUTTON_ADD_OTHER": "Add Other Artifact" - ,"=========== IMPORT VF ===========": "", +,"=========== IMPORT VF ===========": "", "IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_TITLE": "Create VF", "IMPORT_VF_MESSAGE_CREATE_TAKES_LONG_TIME_DESCRIPTION": "Your VF is being created.<br/>It can take up to 10 minutes.<br/>When done, you can view it in SDC Catalog.", "IMPORT_VF_MESSAGE_CREATE_FINISHED_TITLE": "Create/Update", @@ -501,6 +500,33 @@ "PLUGIN_NOT_CONNECTED_ERROR_MAIN": "The \"{{pluginName}}\" plugin is currently unavailable.", "PLUGIN_NOT_CONNECTED_ERROR_SUB": "Please try again later.", + "============SERVICE CONSUMPTION=============" : "", + "CONSUMPTION_TYPE": "Type", + "CONSUMPTION_SOURCE": "Source", + "CONSUMPTION_STATIC_VALUES": "Static Values", + "CONSUMPTION_EXPAND_ALL": "Expand All", + "CONSUMPTION_COLLAPSE_ALL": "Collapse All", + "CONSUMPTION_NO_INPUTS_TO_SHOW": "NO INPUTS TO SHOW", + "CONSUMPTION_NO_OPERATIONS_TO_SHOW": "NO OPERATIONS TO SHOW", + + "============= SERVICE DEPENDENCY ============" : "", + "SERVICE_DEPENDENCY_ADD_RULE": "Add Rule", + "SERVICE_DEPENDENCY_UPDATE_RULE": "Update Rule", + "SERVICE_DEPENDENCY_DELETE_RULE": "Delete Rule", + "SERVICE_DEPENDENCY_DELETE_RULE_MSG": "Are you sure you want to delete this rule?", + "SERVICE_DEPENDENCY_UNCHECK_TITLE": "Remove Dependency", + "SERVICE_DEPENDENCY_UNCHECK_TEXT": "Unchecking \"Mark as Dependent\" will remove dependency and erase all the dependency rules. Are you sure you want to remove dependency?", + + "============= COMPOSITION DETAILS TAB ======" : "", + "DETAILS_TAB_CHANGE_VERSION_MODAL_TITLE": "Change Version", + "DETAILS_TAB_CHANGE_VERSION_MODAL_MSG": "Are you sure you want to change the version?\nIt will affect Service-Consumption and Service-Dependencies data", + + "============= Generic Modal Btn ============" : "", + "MODAL_APPROVE" : "Yes", + "MODAL_SAVE" : "Save", + "MODAL_CREATE" : "Create", + "MODAL_CANCEL" : "Cancel", + "MODAL_DELETE" : "Delete", "=========== POLICY AND GROUPS ===========": "", "ADD_TARGETS" : "Add Targets", "ADD_MEMBERS" : "Add Members", diff --git a/catalog-ui/src/assets/styles/images/service-proxy-icons/dependent.png b/catalog-ui/src/assets/styles/images/service-proxy-icons/dependent.png Binary files differnew file mode 100644 index 0000000000..603093ec62 --- /dev/null +++ b/catalog-ui/src/assets/styles/images/service-proxy-icons/dependent.png diff --git a/catalog-ui/src/assets/styles/images/service-proxy-icons/uncertified_dependent.png b/catalog-ui/src/assets/styles/images/service-proxy-icons/uncertified_dependent.png Binary files differnew file mode 100644 index 0000000000..26ebe6016a --- /dev/null +++ b/catalog-ui/src/assets/styles/images/service-proxy-icons/uncertified_dependent.png diff --git a/catalog-ui/src/assets/styles/mixins.less b/catalog-ui/src/assets/styles/mixins.less index e71f3444aa..43bd2a5f44 100644 --- a/catalog-ui/src/assets/styles/mixins.less +++ b/catalog-ui/src/assets/styles/mixins.less @@ -220,6 +220,32 @@ user-select: text; } +.multiline-ellipsis(@lineHeight: 1.3em, @lineCount: 2, @bgColor: @main_color_p){ + overflow: hidden; + position: relative; + line-height: @lineHeight; + max-height: @lineHeight * @lineCount; + text-align: left; + word-break: normal; + white-space: normal; + padding-right: 1em; + &:before { + content: '...'; + position: absolute; + right: 3px; + bottom: 0; + } + &:after { + content: ''; + position: absolute; + right: 0; + width: 1em; + height: 1em; + margin-top: 0.2em; + background: @bgColor; + } +} + .square-icon() { display: inline-block; diff --git a/catalog-ui/src/assets/styles/mixins_old.less b/catalog-ui/src/assets/styles/mixins_old.less index 29c9574565..7deed223df 100644 --- a/catalog-ui/src/assets/styles/mixins_old.less +++ b/catalog-ui/src/assets/styles/mixins_old.less @@ -243,6 +243,7 @@ .t_15 {.font-color > .t; .font-type > ._15;} /* Added by ikram - */ +.s_1 {.font-color > .s; .font-type > ._1;} .s_12 {.font-color > .s; .font-type > ._12;} .z_9 {.font-color > .z; .font-type > ._9;} diff --git a/catalog-ui/src/assets/styles/sprite.less b/catalog-ui/src/assets/styles/sprite.less index 3e56a0163b..383a830b00 100644 --- a/catalog-ui/src/assets/styles/sprite.less +++ b/catalog-ui/src/assets/styles/sprite.less @@ -135,6 +135,9 @@ .sprite-new.properties:active { background-position: -350px -671px; width: 19px; height: 19px;} .active > .sprite-new.properties { background-position: -350px -671px; width: 19px; height: 19px;} +.sprite-new.dependencies-icon { background-position: -751px -693px; width: 19px; height: 19px; opacity: 0.7;} +.sprite-new.dependencies:active { background-position: -751px -693px; width: 19px; height: 19px;} + .sprite-new.distribution-bth { background-position: -399px -716px; width: 55px; height: 21px;} .sprite-new.distribution-bth.disable { background-position: -464px -716px; width: 55px; height: 21px;} |