diff options
author | Michael Lando <ml636r@att.com> | 2018-03-04 14:53:33 +0200 |
---|---|---|
committer | Michael Lando <ml636r@att.com> | 2018-03-07 13:19:05 +0000 |
commit | a5445100050e49e83f73424198d73cd72d672a4d (patch) | |
tree | cacf4df817df31be23e4e790d1dda857bdae061e /catalog-ui/src | |
parent | 51157f92c21976cba4914c378aaa3cba49826931 (diff) |
Sync Integ to Master
Change-Id: I71e3acc26fa612127756ac04073a522b9cc6cd74
Issue-ID: SDC-977
Signed-off-by: Gitelman, Tal (tg851x) <tg851x@intl.att.com>
Diffstat (limited to 'catalog-ui/src')
224 files changed, 4615 insertions, 879 deletions
diff --git a/catalog-ui/src/app/app.ts b/catalog-ui/src/app/app.ts index e44e4eecb6..24665cccbc 100644 --- a/catalog-ui/src/app/app.ts +++ b/catalog-ui/src/app/app.ts @@ -7,9 +7,9 @@ * 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. @@ -22,6 +22,7 @@ //import 'restangular'; //import 'angular-ui-router'; +import * as _ from "lodash"; import "reflect-metadata"; import 'ng-infinite-scroll'; import './modules/filters.ts'; @@ -57,10 +58,8 @@ import {ComponentServiceNg2} from "./ng2/services/component-services/component.s import {ComponentMetadata} from "./models/component-metadata"; import {Categories} from "./models/categories"; import {IUserProperties} from "./models/user"; -import {SearchWithAutoCompleteComponent} from "./ng2/components/ui/search-with-autocomplete/search-with-autocomplete.component"; import {PluginFrameComponent} from "./ng2/components/ui/plugin/plugin-frame.component"; - let moduleName:string = 'sdcApp'; let viewModelsModuleName:string = 'Sdc.ViewModels'; let directivesModuleName:string = 'Sdc.Directives'; @@ -146,13 +145,6 @@ _.each(hostedApplications, (hostedApp)=> { // ===================== Hosted applications section ==================== export const ng1appModule:ng.IModule = angular.module(moduleName, dependentModules); -angular.module('sdcApp').directive('propertiesAssignment', downgradeComponent({component: PropertiesAssignmentComponent}) as angular.IDirectiveFactory); -angular.module('sdcApp').directive('ng2SearchWithAutocomplete', - downgradeComponent({ - component: SearchWithAutoCompleteComponent, - inputs: ['searchPlaceholder', 'searchBarClass', 'autoCompleteValues'], - outputs: ['searchChanged', 'searchButtonClicked'] - }) as angular.IDirectiveFactory); angular.module('sdcApp').directive('pluginFrame', downgradeComponent( {component: PluginFrameComponent, inputs: ['plugin', 'queryParams'], outputs: ['onLoadingDone']} ) as angular.IDirectiveFactory); ng1appModule.config([ @@ -616,7 +608,6 @@ ng1appModule.value('TagValidationPattern', /^[\s\w_.-]{1,50}$/); ng1appModule.value('VendorReleaseValidationPattern', /^[\x20-\x21\x23-\x29\x2B-\x2E\x30-\x39\x3B\x3D\x40-\x5B\x5D-\x7B\x7D-\xFF]{1,25}$/); ng1appModule.value('VendorNameValidationPattern', /^[\x20-\x21\x23-\x29\x2B-\x2E\x30-\x39\x3B\x3D\x40-\x5B\x5D-\x7B\x7D-\xFF]{1,60}$/); ng1appModule.value('VendorModelNumberValidationPattern', /^[\x20-\x21\x23-\x29\x2B-\x2E\x30-\x39\x3B\x3D\x40-\x5B\x5D-\x7B\x7D-\xFF]{1,65}$/); -ng1appModule.value('ServiceTypeAndRoleValidationPattern', /^[\x20-\x21\x23-\x29\x2B-\x2E\x30-\x39\x3B\x3D\x40-\x5B\x5D-\x7B\x7D-\xFF]{1,256}$/); ng1appModule.value('ContactIdValidationPattern', /^[\s\w-]{1,50}$/); ng1appModule.value('UserIdValidationPattern', /^[\s\w-]{1,50}$/); ng1appModule.value('ProjectCodeValidationPattern', /^[\s\w-]{5,50}$/); @@ -670,7 +661,6 @@ ng1appModule.run([ AngularJSBridge, $templateCache:ng.ITemplateCacheService):void => { $templateCache.put('notification-custom-template.html', require('./view-models/shared/notification-custom-template.html')); - $templateCache.put('notification-custom-template.html', require('./view-models/shared/notification-custom-template.html')); //handle cache data - version let initAsdcVersion:Function = ():void => { diff --git a/catalog-ui/src/app/directives/capabilities-and-requirements/requirement/requirements-list-directive.ts b/catalog-ui/src/app/directives/capabilities-and-requirements/requirement/requirements-list-directive.ts index 14cafa98c4..cda2f9723e 100644 --- a/catalog-ui/src/app/directives/capabilities-and-requirements/requirement/requirements-list-directive.ts +++ b/catalog-ui/src/app/directives/capabilities-and-requirements/requirement/requirements-list-directive.ts @@ -28,6 +28,7 @@ * Created by obarda on 1/8/2017. */ 'use strict'; +import * as _ from "lodash"; import {RequirementsGroup, Component, Relationship, RelationshipModel} from "app/models"; export interface IRequirementsListScope extends ng.IScope { diff --git a/catalog-ui/src/app/directives/ecomp-header/ecomp-header.ts b/catalog-ui/src/app/directives/ecomp-header/ecomp-header.ts index f73c82a190..76bc1692b4 100644 --- a/catalog-ui/src/app/directives/ecomp-header/ecomp-header.ts +++ b/catalog-ui/src/app/directives/ecomp-header/ecomp-header.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {IAppConfigurtaion, User, IUser} from "app/models"; import {IUserProperties} from "../../models/user"; import {UserService} from "../../ng2/services/user.service"; diff --git a/catalog-ui/src/app/directives/export-json-to-excel/export-json-to-excel.ts b/catalog-ui/src/app/directives/export-json-to-excel/export-json-to-excel.ts index c4800fc0b4..25e7143e64 100644 --- a/catalog-ui/src/app/directives/export-json-to-excel/export-json-to-excel.ts +++ b/catalog-ui/src/app/directives/export-json-to-excel/export-json-to-excel.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {FileUtils} from "app/utils"; export interface IJsonExportExcelScope extends ng.IScope { diff --git a/catalog-ui/src/app/directives/file-opener/file-opener.ts b/catalog-ui/src/app/directives/file-opener/file-opener.ts index 4abd2573bb..8240d07958 100644 --- a/catalog-ui/src/app/directives/file-opener/file-opener.ts +++ b/catalog-ui/src/app/directives/file-opener/file-opener.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; export interface IFileOpenerScope extends ng.IScope { importFile:any; diff --git a/catalog-ui/src/app/directives/file-upload/file-upload.ts b/catalog-ui/src/app/directives/file-upload/file-upload.ts index 5a8fd87849..4902741927 100644 --- a/catalog-ui/src/app/directives/file-upload/file-upload.ts +++ b/catalog-ui/src/app/directives/file-upload/file-upload.ts @@ -22,6 +22,7 @@ * Created by obarda on 1/27/2016. */ 'use strict'; +import * as _ from "lodash"; import {IAppConfigurtaion} from "app/models"; export class FileUploadModel { diff --git a/catalog-ui/src/app/directives/graphs-v2/common/common-graph-utils.ts b/catalog-ui/src/app/directives/graphs-v2/common/common-graph-utils.ts index 2a7fd74e53..81d41509e7 100644 --- a/catalog-ui/src/app/directives/graphs-v2/common/common-graph-utils.ts +++ b/catalog-ui/src/app/directives/graphs-v2/common/common-graph-utils.ts @@ -18,8 +18,11 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {CommonNodeBase, CompositionCiLinkBase, RelationshipModel, Relationship, CompositionCiNodeBase, NodesFactory, LinksFactory} from "app/models"; import {GraphUIObjects} from "app/utils"; +import {CompositionCiServicePathLink} from "app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link"; +import {Requirement, Capability} from "app/models"; /** * Created by obarda on 12/21/2016. */ @@ -56,11 +59,11 @@ export class CommonGraphUtils { classes: compositionGraphNode.classes }); - if(!node.data().isUcpe) { //ucpe should not have tooltip - this.initNodeTooltip(node); - } - return node; - }; + if(!node.data().isUcpe) { //ucpe should not have tooltip + this.initNodeTooltip(node); + } + return node; + }; /** * The function will create a component instance node by the componentInstance position. @@ -135,33 +138,30 @@ export class CommonGraphUtils { }; /** - * + * Returns relation source and target nodes. * @param nodes - all nodes in graph in order to find the edge connecting the two nodes * @param fromNodeId * @param toNodeId - * @returns {boolean} true/false if the edge is certified (from node and to node are certified) + * @returns [source, target] array of source node and target node. */ - public isRelationCertified(nodes:Cy.CollectionNodes, fromNodeId:string, toNodeId:string):boolean { - let resourceTemp = _.filter(nodes, function (node:Cy.CollectionFirst) { - return node.data().id === fromNodeId || node.data().id === toNodeId; - }); - let certified:boolean = true; - - _.forEach(resourceTemp, (item) => { - certified = certified && item.data().certified; - }); - - return certified; + public getRelationNodes(nodes:Cy.CollectionNodes, fromNodeId:string, toNodeId:string) { + return [ + _.find(nodes, (node:Cy.CollectionFirst) => node.data().id === fromNodeId), + _.find(nodes, (node:Cy.CollectionFirst) => node.data().id === toNodeId) + ]; } /** * Add link to graph - only draw the link * @param cy * @param link + * @param getRelationRequirementCapability */ - public insertLinkToGraph = (cy:Cy.Instance, link:CompositionCiLinkBase) => { - - if (!this.isRelationCertified(cy.nodes(), link.source, link.target)) { + public insertLinkToGraph = (cy:Cy.Instance, link:CompositionCiLinkBase, getRelationRequirementCapability:Function) => { + const relationNodes = this.getRelationNodes(cy.nodes(), link.source, link.target); + const sourceNode:CompositionCiNodeBase = relationNodes[0] && relationNodes[0].data(); + const targetNode:CompositionCiNodeBase = relationNodes[1] && relationNodes[1].data(); + if ((sourceNode && !sourceNode.certified) || (targetNode && !targetNode.certified)) { link.classes = 'not-certified-link'; } let linkElement = cy.add({ @@ -169,32 +169,85 @@ export class CommonGraphUtils { data: link, classes: link.classes }); - this.initLinkTooltip(linkElement, link); + const getLinkRequirementCapability = () => + getRelationRequirementCapability(link.relation.relationships[0], sourceNode.componentInstance, targetNode.componentInstance); + this.initLinkTooltip(linkElement, link.relation.relationships[0], getLinkRequirementCapability); }; /** + * Add service path link to graph - only draw the link + * @param cy + * @param link + */ + public insertServicePathLinkToGraph = (cy:Cy.Instance, link:CompositionCiServicePathLink) => { + let linkElement = cy.add({ + group: 'edges', + data: link, + classes: link.classes + }); + this.initServicePathTooltip(linkElement, link); + }; + + /** + * Returns function for the link tooltip content + * @param {Relationship} linkRelation + * @param {Requirement} requirement + * @param {Capability} capability + * @returns {() => string} + * @private + */ + private _getLinkTooltipContent(linkRelation:Relationship, requirement?:Requirement, capability?:Capability):string { + return '<div class="line">' + + '<span class="req-cap-label">R: </span>' + + '<span>' + (requirement ? requirement.getTitle() : linkRelation.relation.requirement) + '</span>' + + '</div>' + + '<div class="line">' + + '<div class="sprite-new link-tooltip-arrow"></div>' + + '<span class="req-cap-label">C: </span>' + + '<span>' + (capability ? capability.getTitle() : linkRelation.relation.capability) + '</span>' + + '</div>'; + } + + /** * This function will init qtip tooltip on the link - * @params linkElement - the link we want the tooltip to apply on, + * @param linkElement - the link we want the tooltip to apply on, + * @param link + * @param getLinkRequirementCapability * link - the link obj */ - public initLinkTooltip(linkElement:Cy.CollectionElements, link:CompositionCiLinkBase) { + public initLinkTooltip(linkElement:Cy.CollectionElements, link:Relationship, getLinkRequirementCapability:Function) { + const content = () => this._getLinkTooltipContent(link); // base tooltip content without owner names + const render = (event, api) => { + // on render (called once at first show), get the link requirement and capability and change to full tooltip content (with owner names) + getLinkRequirementCapability().then((linkReqCap) => { + const fullContent = () => this._getLinkTooltipContent(link, linkReqCap.requirement, linkReqCap.capability); + api.set('content.text', fullContent); + }); + }; + linkElement.qtip(this.prepareInitTooltipData({content, events: {render}})); + }; - let opts = { - content: function () { - return '<div class="line">' + - '<span class="req-cap-label">R: </span>' + - '<span>'+ link.relation.relationships[0].relation.requirement + '</span>' + - '</div>' + - '<div class="line">' + - '<div class="sprite-new link-tooltip-arrow"></div>' + - '<span class="req-cap-label">C: </span>' + - '<span>' + link.relation.relationships[0].relation.capability + '</span>' + - '</div>'; - }, + /** + * + * @param linkElement + * @param link + */ + public initServicePathTooltip(linkElement:Cy.CollectionElements, link:CompositionCiServicePathLink) { + let content = function () { + return '<div class="line">' + + '<div>'+link.pathName+'</div>' + + '</div>'; + }; + linkElement.qtip(this.prepareInitTooltipData({content})); + }; + + private prepareInitTooltipData(options?:Object) { + return _.merge({ position: { my: 'top center', at: 'bottom center', - adjust: {x:0, y:0} + adjust: {x:0, y:0}, + effect: false }, style: { classes: 'qtip-dark qtip-rounded qtip-custom link-qtip', @@ -208,24 +261,24 @@ export class CommonGraphUtils { delay: 1000 }, hide: {event: 'mouseout mousedown'}, - includeLabels: true - }; - - linkElement.qtip(opts); - }; + includeLabels: true, + events: {} + }, options); + } /** * go over the relations and draw links on the graph * @param cy * @param instancesRelations + * @param getRelationRequirementCapability - function to get requirement and capability of a relation */ - public initGraphLinks(cy:Cy.Instance, instancesRelations:Array<RelationshipModel>) { + public initGraphLinks(cy:Cy.Instance, instancesRelations:Array<RelationshipModel>, getRelationRequirementCapability:Function) { if (instancesRelations) { _.forEach(instancesRelations, (relationshipModel:RelationshipModel) => { _.forEach(relationshipModel.relationships, (relationship:Relationship) => { let linkToCreate = this.LinksFactory.createGraphLink(cy, relationshipModel, relationship); - this.insertLinkToGraph(cy, linkToCreate); + this.insertLinkToGraph(cy, linkToCreate, getRelationRequirementCapability); }); }); } @@ -297,8 +350,8 @@ export class CommonGraphUtils { public getCytoscapeNodePosition = (cy:Cy.Instance, event:IDragDropEvent):Cy.Position => { let targetOffset = $(event.target).offset(); - let x = event.pageX - targetOffset.left; - let y = event.pageY - targetOffset.top; + let x = (event.pageX - targetOffset.left) / cy.zoom(); + let y = (event.pageY - targetOffset.top) / cy.zoom(); return this.HTMLCoordsToCytoscapeCoords(cy.extent(), { x: x, @@ -316,14 +369,14 @@ export class CommonGraphUtils { return nodePosition; } - /** - * Generic function that can be used for any html elements overlaid on canvas - * Returns the html position of a node on canvas, including left palette and header offsets. Option to pass in additional offset to add to return position. - * @param node - * @param additionalOffset - * @returns {Cy.Position} - - public getNodePositionWithOffset = (node:Cy.CollectionFirstNode, additionalOffset?:Cy.Position): Cy.Position => { + /** + * Generic function that can be used for any html elements overlaid on canvas + * Returns the html position of a node on canvas, including left palette and header offsets. Option to pass in additional offset to add to return position. + * @param node + * @param additionalOffset + * @returns {Cy.Position} + + public getNodePositionWithOffset = (node:Cy.CollectionFirstNode, additionalOffset?:Cy.Position): Cy.Position => { if(!additionalOffset) additionalOffset = {x: 0, y:0}; let nodePosition = node.renderedPosition(); @@ -334,13 +387,13 @@ export class CommonGraphUtils { return posWithOffset; };*/ - /** - * return true/false if first node contains in second - this used in order to verify is node is entirely inside ucpe - * @param firstBox - * @param secondBox - * @returns {boolean} - */ - public isFirstBoxContainsInSecondBox(firstBox:Cy.BoundingBox, secondBox:Cy.BoundingBox) { + /** + * return true/false if first node contains in second - this used in order to verify is node is entirely inside ucpe + * @param firstBox + * @param secondBox + * @returns {boolean} + */ + public isFirstBoxContainsInSecondBox(firstBox:Cy.BoundingBox, secondBox:Cy.BoundingBox) { return firstBox.x1 > secondBox.x1 && firstBox.x2 < secondBox.x2 && firstBox.y1 > secondBox.y1 && firstBox.y2 < secondBox.y2; @@ -385,50 +438,50 @@ export class CommonGraphUtils { */ public nodeLocationsCompatible(cy:Cy.Instance, node1:Cy.CollectionFirstNode, node2:Cy.CollectionFirstNode) { - let ucpe = cy.nodes('[?isUcpe]'); - if(!ucpe.length){ return true; } - if(node1.data().isUcpePart || node2.data().isUcpePart) { return true; } + let ucpe = cy.nodes('[?isUcpe]'); + if(!ucpe.length){ return true; } + if(node1.data().isUcpePart || node2.data().isUcpePart) { return true; } return (this.isFirstBoxContainsInSecondBox(node1.boundingbox(), ucpe.boundingbox()) == this.isFirstBoxContainsInSecondBox(node2.boundingbox(), ucpe.boundingbox())); } - /** - * This function will init qtip tooltip on the node - * @param node - the node we want the tooltip to apply on - */ - public initNodeTooltip(node:Cy.CollectionNodes) { - - let opts = { - content: function () { - return this.data('name'); - }, - position: { - my: 'top center', - at: 'bottom center', - adjust: {x:0, y:-5} - }, - style: { - classes: 'qtip-dark qtip-rounded qtip-custom', - tip: { - width: 16, - height: 8 - } - }, - show: { - event: 'mouseover', - delay: 1000 - }, - hide: {event: 'mouseout mousedown'}, - includeLabels: true - }; - - if (node.data().isUcpePart){ //fix tooltip positioning for UCPE-cps - opts.position.adjust = {x:0, y:20}; - } - - node.qtip(opts); + /** + * This function will init qtip tooltip on the node + * @param node - the node we want the tooltip to apply on + */ + public initNodeTooltip(node:Cy.CollectionNodes) { + + let opts = { + content: function () { + return this.data('name'); + }, + position: { + my: 'top center', + at: 'bottom center', + adjust: {x:0, y:-5} + }, + style: { + classes: 'qtip-dark qtip-rounded qtip-custom', + tip: { + width: 16, + height: 8 + } + }, + show: { + event: 'mouseover', + delay: 1000 + }, + hide: {event: 'mouseout mousedown'}, + includeLabels: true }; + + if (node.data().isUcpePart){ //fix tooltip positioning for UCPE-cps + opts.position.adjust = {x:0, y:20}; + } + + node.qtip(opts); + }; } CommonGraphUtils.$inject = ['NodesFactory', 'LinksFactory']; 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 36c75c55fc..596dcecc13 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 @@ -176,6 +176,17 @@ export class ComponentInstanceNodesStyle { } }, { + selector: '.vl-link-1', + css: { + 'width': 3, + 'line-color': GraphColors.ACTIVE_LINK, + 'curve-style': 'unbundled-bezier', + 'target-arrow-color': '#3b7b9b', + 'target-arrow-shape': 'triangle', + 'control-point-step-size': 30 + } + }, + { selector: '.ucpe-host-link', css: { 'width': 0 @@ -196,6 +207,17 @@ export class ComponentInstanceNodesStyle { }, { + selector: '.service-path-link', + css: { + 'width': 2, + 'line-color': GraphColors.SERVICE_PATH_LINK, + 'target-arrow-color': GraphColors.SERVICE_PATH_LINK, + 'target-arrow-shape': 'triangle', + 'curve-style': 'bezier', + 'control-point-step-size': 30 + } + }, + { selector: '.not-certified', css: { 'shape': 'rectangle', 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 cdb163bda1..2144ecfbfa 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 @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Match, LinkMenu, @@ -28,13 +29,15 @@ import { Relationship, PropertyModel, Component, + Service, ConnectRelationModel, CompositionCiNodeBase, CompositionCiNodeVl, ModalModel, ButtonModel, NodesFactory/*, - AssetPopoverObj*/ + AssetPopoverObj*/, + Point } from "app/models"; import {ComponentInstanceFactory, ComponentFactory, GRAPH_EVENTS, GraphColors} from "app/utils"; import {EventListenerService, LoaderService} from "app/services"; @@ -60,13 +63,32 @@ import {ConnectionPropertiesViewComponent} from "../../../ng2/pages/connection-w import {ComponentInstanceServiceNg2} from "../../../ng2/services/component-instance-services/component-instance.service"; import {EVENTS} from "../../../utils/constants"; import {PropertyBEModel} from "../../../models/properties-inputs/property-be-model"; +import {ComponentType} from "app/utils"; +import {ForwardingPath} from "app/models/forwarding-path"; +import {ServicePathGraphUtils} from "./utils/composition-graph-service-path-utils"; +import {CompositionCiServicePathLink} from "app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link"; +import { ZoneConfig, ZoneInstanceConfig, ZoneInstanceMode } from "app/models/graph/zones/zone-child"; +import { PoliciesService } from "app/ng2/services/policies.service"; +import { PaletteAnimationComponent } from "app/ng2/components/ui/palette-animation/palette-animation.component"; +import { CompositionGraphZoneUtils } from "./utils/composition-graph-zone-utils"; +import {LeftPaletteMetadataTypes} from "../../../models/components/displayComponent"; -interface ICompositionGraphScope extends ng.IScope { + +export interface ICompositionGraphScope extends ng.IScope { component:Component; isLoading: boolean; isViewOnly: boolean; withSidebar: boolean; + + //zones + newZoneInstance; + zoneTagMode:string; + activeZoneInstance:ZoneInstanceConfig; + zones:any; + zoneInstanceModeChanged(newMode:ZoneInstanceMode, instance:ZoneInstanceConfig, zoneId:string); + clickOutsideZoneInstance():void; + // Link menu - create link menu relationMenuDirectiveObj:ConnectRelationModel; isLinkMenuOpen:boolean; @@ -76,6 +98,7 @@ interface ICompositionGraphScope extends ng.IScope { //modify link menu - for now only delete menu relationMenuTimeout:ng.IPromise<any>; linkMenuObject:LinkMenu; + isOnDrag: boolean; //left palette functions callbacks dropCallback(event:JQueryEventObject, ui:any):void; @@ -95,7 +118,12 @@ interface ICompositionGraphScope extends ng.IScope { highlightSearchMatches(searchTerm: string): void; canvasMenuProps:any; - + + createOrUpdateServicePath(data: any):void; + deletePathsOnCy():void; + drawPathOnCy(data: ForwardingPath):void; + selectedPathId: string; + /*//asset popover menu assetPopoverObj:AssetPopoverObj; assetPopoverOpen:boolean; @@ -124,10 +152,13 @@ export class CompositionGraph implements ng.IDirective { private commonGraphUtils:CommonGraphUtils, private matchCapabilitiesRequirementsUtils:MatchCapabilitiesRequirementsUtils, private CompositionGraphPaletteUtils:CompositionGraphPaletteUtils, + private compositionGraphZoneUtils:CompositionGraphZoneUtils, private ComponentServiceNg2: ComponentServiceNg2, private ModalServiceNg2: ModalService, private ConnectionWizardServiceNg2: ConnectionWizardService, - private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2) { + private ComponentInstanceServiceNg2: ComponentInstanceServiceNg2, + private servicePathGraphUtils: ServicePathGraphUtils, + private policiesService:PoliciesService) { } @@ -140,7 +171,6 @@ export class CompositionGraph implements ng.IDirective { }; link = (scope:ICompositionGraphScope, el:JQuery) => { - this.loadGraph(scope, el); if(scope.component.componentInstances && scope.component.componentInstancesRelations) { @@ -164,15 +194,16 @@ export class CompositionGraph implements ng.IDirective { private loadGraphData = (scope:ICompositionGraphScope) => { this.initGraphNodes(scope.component.componentInstances, scope.isViewOnly); - this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations); + this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations, scope.component.getRelationRequirementCapability.bind(scope.component)); this.commonGraphUtils.initUcpeChildren(this._cy); + this.compositionGraphZoneUtils.initPolicyInstances(scope.zones.policy, scope.component.policies); } private loadGraph = (scope:ICompositionGraphScope, el:JQuery) => { - let graphEl = el.find('.sdc-composition-graph-wrapper'); this.initGraph(graphEl, scope.isViewOnly); this.initDropZone(scope); + this.initZones(scope); this.registerCytoscapeGraphEvents(scope); this.registerCustomEvents(scope, el); this.initViewMode(scope.isViewOnly); @@ -210,6 +241,12 @@ export class CompositionGraph implements ng.IDirective { private registerCustomEvents(scope:ICompositionGraphScope, el:JQuery) { this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (leftPaletteComponent:LeftPaletteComponent) => { + if(scope.isOnDrag || + leftPaletteComponent.categoryType === LeftPaletteMetadataTypes.Group || + leftPaletteComponent.categoryType === LeftPaletteMetadataTypes.Policy) { + return; + } + this.$log.info(`composition-graph::registerEventServiceEvents:: palette hover on component: ${leftPaletteComponent.uniqueId}`); let nodesData = this.NodesGraphUtils.getAllNodesData(this._cy.nodes()); @@ -240,6 +277,33 @@ export class CompositionGraph implements ng.IDirective { }); }); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ADD_COMPONENT_INSTANCE_ZONE_START, (component:Component, paletteComponent:LeftPaletteComponent, startPosition:Point) => { + this.LoaderService.showLoader('composition-graph'); + + let zoneType:string = LeftPaletteMetadataTypes[paletteComponent.categoryType].toLowerCase(); + scope.zones[zoneType].showZone = true; + if(scope.minifyZone) scope.minifyZone = false; + + this.policiesService.createPolicyInstance(component.componentType, component.uniqueId, paletteComponent.type).subscribe((newInstance)=>{ + + this.LoaderService.hideLoader('composition-graph'); + scope.newZoneInstance = newInstance; + this.compositionGraphZoneUtils.showAnimationToZone(startPosition, zoneType); + }, (error) => { + this.LoaderService.hideLoader('composition-graph'); + }); + }); + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_FINISH_ANIMATION_ZONE, () => { + if(scope.newZoneInstance){ + this.compositionGraphZoneUtils.addInstanceToZone(scope.zones['policy'], scope.newZoneInstance); + } + }) + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ZONE_SIZE_CHANGE, () => { + scope.minifyZone = true; + }) + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => { this._cy.emit('hidehandles'); this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy); @@ -362,17 +426,11 @@ export class CompositionGraph implements ng.IDirective { .updateInstanceCapabilityProperties( scope.component, this.ConnectionWizardServiceNg2.selectedMatch.toNode, - this.ConnectionWizardServiceNg2.selectedMatch.capability.type, - this.ConnectionWizardServiceNg2.selectedMatch.capability.name, + this.ConnectionWizardServiceNg2.selectedMatch.capability, capabilityPropertiesBE ) .subscribe((response) => { console.log("Update resource instance capability properties response: ", response); - response.forEach((resProperty) => { - this.ConnectionWizardServiceNg2.selectedMatch.capabilityProperties.find((property) => { - return property.uniqueId == resProperty.uniqueId; - }).value = resProperty.value; - }); this.ConnectionWizardServiceNg2.changedCapabilityProperties = []; resolve(capabilityPropertiesBE); }); @@ -400,6 +458,16 @@ export class CompositionGraph implements ng.IDirective { }); }; + scope.createOrUpdateServicePath = (data:any) => { + this.servicePathGraphUtils.createOrUpdateServicePath(scope, data); + }; + scope.deletePathsOnCy = () => { + this.servicePathGraphUtils.deletePathsFromGraph(this._cy, <Service> scope.component); + }; + scope.drawPathOnCy = (data: ForwardingPath) => { + this.servicePathGraphUtils.drawPath(this._cy, data, <Service> scope.component); + }; + scope.viewRelation = (link:Cy.CollectionEdges) => { scope.hideRelationMenu(); @@ -408,41 +476,9 @@ export class CompositionGraph implements ng.IDirective { const targetNode:CompositionCiNodeBase = link.target().data(); const relationship:Relationship = linkData.relation.relationships[0]; - let capability:Capability; - _.some(_.values(targetNode.componentInstance.capabilities), (capGroup) => { - //item.uniqueId + item.ownerId + item.name === (selectedReqOrCapModel.uniqueId + selectedReqOrCapModel.ownerId + selectedReqOrCapModel.name) - capability = _.find<Capability>(_.values<Capability>(capGroup), (cap:Capability) => ( - cap.uniqueId === relationship.relation.capabilityUid && - cap.ownerId === relationship.relation.capabilityOwnerId && - cap.name === relationship.relation.capability - )); - return capability; - }); - let requirement:Requirement; - _.some(_.values(sourceNode.componentInstance.requirements), (reqGroup) => { - requirement = _.find<Requirement>(_.values<Requirement>(reqGroup), (req:Requirement) => ( - req.uniqueId === relationship.relation.requirementUid && - req.ownerId === relationship.relation.requirementOwnerId && - req.name === relationship.relation.requirement - )); - return requirement; - }); - - new Promise<{capability:Capability, requirement:Requirement}>((resolve, reject) => { - if (capability && requirement) { - resolve({capability, requirement}); - } - else { - scope.component.fetchRelation(relationship.relation.id).then((fetchedRelation) => { - resolve({ - capability: fetchedRelation.relationships[0].capability, - requirement: fetchedRelation.relationships[0].requirement - }); - }, reject); - } - }).then((objReqCap) => { - capability = objReqCap.capability; - requirement = objReqCap.requirement; + scope.component.getRelationRequirementCapability(relationship, sourceNode.componentInstance, targetNode.componentInstance).then((objReqCap) => { + const capability = objReqCap.capability; + const requirement = objReqCap.requirement; this.ConnectionWizardServiceNg2.currentComponent = scope.component; this.ConnectionWizardServiceNg2.connectRelationModel = new ConnectRelationModel(sourceNode, targetNode, []); @@ -459,11 +495,19 @@ export class CompositionGraph implements ng.IDirective { this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent); modalInstance.instance.open(); - this.ComponentInstanceServiceNg2.getInstanceCapabilityProperties(scope.component, linkData.target, capability.type, capability.name) - .subscribe((response: Array<PropertyModel>) => { - this.ConnectionWizardServiceNg2.selectedMatch.capabilityProperties = response; - this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent); - }, (error) => {}); + new Promise((resolve) => { + if (!this.ConnectionWizardServiceNg2.selectedMatch.capability.properties) { + this.ComponentInstanceServiceNg2.getInstanceCapabilityProperties(scope.component, linkData.target, capability) + .subscribe(() => { + resolve(); + }, (error) => {}); + } else { + resolve(); + } + }).then(() => { + this.ModalServiceNg2.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent); + }) + }, (error) => {}); }; @@ -504,7 +548,7 @@ export class CompositionGraph implements ng.IDirective { this.ConnectionWizardServiceNg2.currentComponent = scope.component; //TODO: init with the selected values this.ConnectionWizardServiceNg2.selectedMatch = null; - + let steps:Array<StepModel> = []; let fromNodeName:string = scope.relationMenuDirectiveObj.fromNode.componentInstance.name; let toNodeName:string = scope.relationMenuDirectiveObj.toNode.componentInstance.name; @@ -523,6 +567,7 @@ export class CompositionGraph implements ng.IDirective { } }); this._cy.on('tapstart', 'node', (event:Cy.EventObject) => { + scope.isOnDrag = true; this._currentlyCLickedNodePosition = angular.copy(event.cyTarget[0].position()); //update node position on drag if (event.cyTarget.data().isUcpe) { this._cy.nodes('.ucpe-cp').unlock(); @@ -592,7 +637,7 @@ export class CompositionGraph implements ng.IDirective { this._cy.on('tapend', (event:Cy.EventObject) => { - + scope.isOnDrag = false; if (event.cyTarget === this._cy) { //On Background clicked if (this._cy.$('node:selected').length === 0) { //if the background click but not dragged this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED); @@ -603,6 +648,9 @@ export class CompositionGraph implements ng.IDirective { else if (event.cyTarget.isEdge()) { //On Edge clicked if (scope.isViewOnly) return; this.CompositionGraphLinkUtils.handleLinkClick(this._cy, event); + if (event.cyTarget.data().type === CompositionCiServicePathLink.LINK_TYPE) { + return; + } this.openModifyLinkMenu(scope, this.CompositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), 6000); } @@ -640,13 +688,6 @@ export class CompositionGraph implements ng.IDirective { }); } - /* - private showNodePopoverMenu = (scope:ICompositionGraphScope, node:Cy.CollectionNodes) => { - - scope.assetPopoverObj = this.NodesGraphUtils.createAssetPopover(this._cy, node, scope.isViewOnly); - scope.assetPopoverOpen = true; - - };*/ private openModifyLinkMenu = (scope:ICompositionGraphScope, linkMenuObject:LinkMenu, timeOutInMilliseconds?:number) => { scope.hideRelationMenu(); this.$timeout(() => { @@ -732,6 +773,49 @@ export class CompositionGraph implements ng.IDirective { } } + + private initZones = (scope:ICompositionGraphScope):void => { + scope.zones = this.compositionGraphZoneUtils.createCompositionZones(); + + scope.zoneInstanceModeChanged = (newMode:ZoneInstanceMode, instance:ZoneInstanceConfig, zoneId:string):void => { + if(scope.zoneTagMode) { //we're in tag mode. + if(instance == scope.activeZoneInstance && newMode == ZoneInstanceMode.TAG){ //we want to toggle tag mode off. + scope.unsetActiveZoneInstance(); + } + } else { + scope.setZoneInstanceMode(newMode, instance, zoneId); + } + }; + + scope.setZoneInstanceMode = (newMode:ZoneInstanceMode, instance:ZoneInstanceConfig, zoneId:string):void => { + instance.mode = newMode; + switch(newMode){ + case ZoneInstanceMode.TAG: { + scope.zoneTagMode = zoneId + "-tagging"; + } + case ZoneInstanceMode.SELECTED: { //case TAG flows into here as well + scope.activeZoneInstance = instance; + break; + } + } + }; + + scope.unsetActiveZoneInstance = ():void => { + scope.activeZoneInstance.mode = ZoneInstanceMode.NONE; + scope.activeZoneInstance = null; + scope.zoneTagMode = null; + }; + + scope.clickOutsideZoneInstance = ():void => { + if(!scope.zoneTagMode) + scope.unsetActiveZoneInstance(); + }; + + }; + + + + public static factory = ($q, $log, $timeout, @@ -746,10 +830,13 @@ export class CompositionGraph implements ng.IDirective { CommonGraphUtils, MatchCapabilitiesRequirementsUtils, CompositionGraphPaletteUtils, + CompositionGraphZoneUtils, ComponentServiceNg2, ModalService, ConnectionWizardService, - ComponentInstanceServiceNg2) => { + ComponentInstanceServiceNg2, + ServicePathGraphUtils, + PoliciesService) => { return new CompositionGraph( $q, $log, @@ -765,10 +852,13 @@ export class CompositionGraph implements ng.IDirective { CommonGraphUtils, MatchCapabilitiesRequirementsUtils, CompositionGraphPaletteUtils, + CompositionGraphZoneUtils, ComponentServiceNg2, ModalService, ConnectionWizardService, - ComponentInstanceServiceNg2); + ComponentInstanceServiceNg2, + ServicePathGraphUtils, + PoliciesService); } } @@ -787,8 +877,11 @@ CompositionGraph.factory.$inject = [ 'CommonGraphUtils', 'MatchCapabilitiesRequirementsUtils', 'CompositionGraphPaletteUtils', + 'CompositionGraphZoneUtils', 'ComponentServiceNg2', 'ModalServiceNg2', 'ConnectionWizardServiceNg2', - 'ComponentInstanceServiceNg2' + 'ComponentInstanceServiceNg2', + 'ServicePathGraphUtils', + 'PoliciesServiceNg2' ]; diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html index bbf2d6bbb2..487e4cb65a 100644 --- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.html @@ -1,5 +1,5 @@ <loader display="isLoading" loader-type="composition-graph"></loader> -<div class="sdc-composition-graph-wrapper" ng-class="{'view-only':isViewOnly}" +<div class="sdc-composition-graph-wrapper {{zoneTagMode}}" ng-class="{'view-only':isViewOnly}" data-drop="true" data-jqyoui-options="{accept: verifyDrop}" data-jqyoui-droppable="{onDrop:'dropCallback', beforeDrop: 'beforeDropCallback'}"> @@ -11,10 +11,37 @@ <menu-list-ng2 [props]="canvasMenuProps"></menu-list-ng2> <div class="w-sdc-search-menu" data-ng-class="{'with-sidebar': withSidebar}"> - <ng2-search-with-autocomplete [search-placeholder]="'Type to search'" [auto-complete-values]="componentInstanceNames" (search-changed)="getAutoCompleteValues($event)" (search-button-clicked)="highlightSearchMatches($event)" - [search-bar-class]="'composition-search'"></ng2-search-with-autocomplete> + + <ng2-service-path-selector + ng-if="component.isService()" + [service]="component" + [draw-path]="drawPathOnCy" + [delete-paths]="deletePathsOnCy" + [selected-path-id]="selectedPathId"> + </ng2-service-path-selector> + <ng2-service-path + ng-if="component.isService()" + [service]="component" + [on-create]="createOrUpdateServicePath"> + </ng2-service-path> + <ng2-search-with-autocomplete + [search-placeholder]="'Type to search'" + [auto-complete-values]="componentInstanceNames" + (search-changed)="getAutoCompleteValues($event)" + (search-button-clicked)="highlightSearchMatches($event)" + [search-bar-class]="'composition-search'"> + </ng2-search-with-autocomplete> <div class="zoom-icons sprite-new canvas-fit-all" data-ng-click="zoomAll()"></div> <div class="zoom-icons sprite-new zoom-plus" data-ng-click="zoom(true)"></div> <div class="zoom-icons sprite-new zoom-minus" data-ng-click="zoom(false)"></div> </div> <!--<asset-popover ng-if="assetPopoverOpen" asset-popover-obj="assetPopoverObj" delete-asset="deleteNode(assetPopoverObj.nodeId)"></asset-popover>--> +<div class="sdc-canvas-zones__wrapper {{zoneTagMode}}" data-ng-class="{'with-sidebar': withSidebar}"> + <ng2-zone-container data-ng-repeat="zoneConfig in zones" [title]="zoneConfig.title" [class]="zoneConfig.type" [count]="zoneConfig.instances.length" [show-zone] = "zoneConfig.showZone" [minify-zone] = "minifyZone"> + <ng2-zone-instance + data-ng-repeat="instance in zoneConfig.instances" clicked-outside="{onClickedOutside: 'clickOutsideZoneInstance()', clickedOutsideEnable: 'activeZoneInstance == instance'}" + [config]="instance" [default-icon-text]="zoneConfig.defaultIconText" [is-active]="activeZoneInstance == instance" [active-instance-mode]="activeZoneInstance && activeZoneInstance.mode" + (mode-change)="zoneInstanceModeChanged($event.newMode, $event.instance, zoneConfig.type)"> + </ng2-zone-instance> + </ng2-zone-container> +</div> diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less index 56c8b5529d..5a6a104670 100644 --- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/composition-graph.less @@ -11,4 +11,28 @@ composition-graph { .view-only{ background-color:rgb(248, 248, 248); } + + .sdc-canvas-zones__wrapper { + position: absolute; + bottom: 10px; + right: 12px; + display:flex; + transition: right 0.2s; + + &.with-sidebar { + right:310px; + } + + ng2-zone-container { + display:flex; + margin-left: 10px; + } + } + + .group-tagging { + cursor: url("/assets/styles/images/canvas-tagging-icons/adding_group.svg"), pointer; + } + .policy-tagging { + cursor: url("/assets/styles/images/canvas-tagging-icons/adding_policy.svg"), pointer; + } } diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts index da7d48e6ae..73e03e954d 100644 --- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-general-utils.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {ComponentInstance, Component, Match, CompositionCiLinkBase, CompositionCiNodeUcpeCp} from "app/models"; import {QueueUtils, Dictionary, GraphUIObjects} from "app/utils"; import {LoaderService} from "app/services"; diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts index 89c5e14602..705367c5f7 100644 --- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts @@ -21,6 +21,7 @@ /** * Created by obarda on 6/28/2016. */ +import * as _ from "lodash"; import {GraphUIObjects} from "app/utils"; import {LoaderService} from "app/services"; import { @@ -39,6 +40,7 @@ import { import {CommonGraphUtils} from "../../common/common-graph-utils"; import {CompositionGraphGeneralUtils} from "./composition-graph-general-utils"; import {MatchCapabilitiesRequirementsUtils} from "./match-capability-requierment-utils"; +import {CompositionCiServicePathLink} from "../../../../models/graph/graph-links/composition-graph-links/composition-ci-service-path-link"; export class CompositionGraphLinkUtils { @@ -86,7 +88,7 @@ export class CompositionGraphLinkUtils { let onSuccess:(response:RelationshipModel) => void = (relation:RelationshipModel) => { link.setRelation(relation); - this.commonGraphUtils.insertLinkToGraph(cy, link); + this.commonGraphUtils.insertLinkToGraph(cy, link, component.getRelationRequirementCapability.bind(component)); }; link.updateLinkDirection(); @@ -212,6 +214,28 @@ export class CompositionGraphLinkUtils { this.createLink(link, cy, component); }; + private handlePathLink(cy:Cy.Instance, event:Cy.EventObject) { + let linkData = event.cyTarget.data(); + let selectedPathId = linkData.pathId; + let pathEdges = cy.collection(`[pathId='${selectedPathId}']`); + if (pathEdges.length > 1) { + setTimeout(() => { + pathEdges.select(); + }, 0); + } + } + + private handleVLLink(event:Cy.EventObject) { + let vl:Cy.CollectionNodes = event.cyTarget[0].target('.vl-node'); + let connectedEdges:Cy.CollectionEdges = vl.connectedEdges(`[type!="${CompositionCiServicePathLink.LINK_TYPE}"]`); + if (vl.length && connectedEdges.length > 1) { + setTimeout(() => { + vl.select(); + connectedEdges.select(); + }, 0); + } + } + /** * Handles click event on links. @@ -224,18 +248,13 @@ export class CompositionGraphLinkUtils { if (cy.$('edge:selected').length > 1 && event.cyTarget[0].selected()) { cy.$(':selected').unselect(); } else { - - let vl:Cy.CollectionNodes = event.cyTarget[0].target('.vl-node'); - let connectedEdges:Cy.CollectionEdges = vl.connectedEdges(); - if (vl.length && connectedEdges.length > 1) { - - setTimeout(() => { - vl.select(); - connectedEdges.select(); - }, 0); + if (event.cyTarget[0].data().type === CompositionCiServicePathLink.LINK_TYPE) { + this.handlePathLink(cy, event); + } + else { + this.handleVLLink(event); } } - } diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts index 449d551cc0..fb1e6650bd 100644 --- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-nodes-utils.ts @@ -18,11 +18,15 @@ * ============LICENSE_END========================================================= */ -import {Component, NodesFactory, ComponentInstance, CompositionCiNodeVl,IAppMenu,AssetPopoverObj} from "app/models"; +import * as _ from "lodash"; +import {Component, NodesFactory, ComponentInstance, CompositionCiNodeVl,IAppMenu,AssetPopoverObj, Service} from "app/models"; import {EventListenerService, LoaderService} from "app/services"; import {GRAPH_EVENTS,ModalsHandler,GraphUIObjects} from "app/utils"; import {CompositionGraphGeneralUtils} from "./composition-graph-general-utils"; import {CommonGraphUtils} from "../../common/common-graph-utils"; +import {CompositionCiServicePathLink} from "app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link"; +import {ServiceGenericResponse} from "app/ng2/services/responses/service-generic-response"; +import {ServiceServiceNg2} from 'app/ng2/services/component-services/service.service'; /** * Created by obarda on 11/9/2016. */ @@ -31,9 +35,10 @@ export class CompositionGraphNodesUtils { private GeneralGraphUtils:CompositionGraphGeneralUtils, private commonGraphUtils:CommonGraphUtils, private eventListenerService:EventListenerService, - private loaderService:LoaderService /*, - private sdcMenu: IAppMenu, - private ModalsHandler: ModalsHandler*/) { + private loaderService:LoaderService, + private serviceService:ServiceServiceNg2, + /*private sdcMenu: IAppMenu, + private ModalsHandler: ModalsHandler*/) { } @@ -88,9 +93,20 @@ export class CompositionGraphNodesUtils { this.handleConnectedVlsToDelete(connectedVls); } + // check whether there is a service path going through this node, and if so clean it from the graph. + let nodeId = nodeToDelete.data().id; + let connectedPathLinks = cy.collection(`[type="${CompositionCiServicePathLink.LINK_TYPE}"][source="${nodeId}"], [type="${CompositionCiServicePathLink.LINK_TYPE}"][target="${nodeId}"]`); + _.forEach(connectedPathLinks, (link, key) => { + cy.remove(`[pathId="${link.data().pathId}"]`); + }); + + // update service path list + this.serviceService.getComponentCompositionData(component).subscribe((response:ServiceGenericResponse) => { + (<Service>component).forwardingPaths = response.forwardingPaths; + }); + //update UI cy.remove(nodeToDelete); - }; let onFailed:(response:any) => void = (response:any) => { @@ -297,5 +313,5 @@ export class CompositionGraphNodesUtils { } - CompositionGraphNodesUtils.$inject = ['NodesFactory', '$log', 'CompositionGraphGeneralUtils', 'CommonGraphUtils', 'EventListenerService', 'LoaderService' /*, 'sdcMenu', 'ModalsHandler'*/] + CompositionGraphNodesUtils.$inject = ['NodesFactory', '$log', 'CompositionGraphGeneralUtils', 'CommonGraphUtils', 'EventListenerService', 'LoaderService', 'ServiceServiceNg2' /*, 'sdcMenu', 'ModalsHandler'*/] diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-service-path-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-service-path-utils.ts new file mode 100644 index 0000000000..ef047d7dd3 --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-service-path-utils.ts @@ -0,0 +1,80 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import * as _ from "lodash"; +import {LoaderService} from "app/services"; +import {CompositionGraphGeneralUtils} from "./composition-graph-general-utils"; +import {ICompositionGraphScope} from "../composition-graph.directive"; +import {ServiceServiceNg2} from 'app/ng2/services/component-services/service.service'; +import {Service} from "../../../../models/components/service"; +import {ForwardingPath} from "app/models/forwarding-path"; +import {ForwardingPathLink} from "app/models/forwarding-path-link"; +import {CompositionCiServicePathLink} from "../../../../models/graph/graph-links/composition-graph-links/composition-ci-service-path-link"; +import {CommonGraphUtils} from "app/directives/graphs-v2/common/common-graph-utils"; + +export class ServicePathGraphUtils { + + constructor( + private loaderService:LoaderService, + private generalGraphUtils:CompositionGraphGeneralUtils, + private serviceService:ServiceServiceNg2, + private commonGraphUtils:CommonGraphUtils + ) {} + + public deletePathsFromGraph(cy: Cy.Instance, service:Service){ + cy.remove(`[type="${CompositionCiServicePathLink.LINK_TYPE}"]`); + } + + public drawPath(cy: Cy.Instance, forwardingPath: ForwardingPath, service:Service) { + let pathElements = forwardingPath.pathElements.listToscaDataDefinition; + + _.forEach(pathElements, (link: ForwardingPathLink) => { + let data:CompositionCiServicePathLink = new CompositionCiServicePathLink(link); + data.source = data.forwardingPathLink.fromNode; + data.target = data.forwardingPathLink.toNode; + data.pathId = forwardingPath.uniqueId; + data.pathName = forwardingPath.name; + this.commonGraphUtils.insertServicePathLinkToGraph(cy, data); + }); + } + + public createOrUpdateServicePath = (scope:ICompositionGraphScope, path: any): void => { + let service = <Service>scope.component; + this.loaderService.showLoader('composition-graph'); + + let onSuccess: (response: ForwardingPath) => void = (response: ForwardingPath) => { + + service.forwardingPaths[response.uniqueId] = response; + scope.selectedPathId = response.uniqueId; + }; + + this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback( + () => this.serviceService.createOrUpdateServicePath(service, path).subscribe(onSuccess), + () => this.loaderService.hideLoader('composition-graph') + ); + }; +} + +ServicePathGraphUtils.$inject = [ + 'LoaderService', + 'CompositionGraphGeneralUtils', + 'ServiceServiceNg2', + 'CommonGraphUtils' +]; diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-zone-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-zone-utils.ts new file mode 100644 index 0000000000..28f2dc85d2 --- /dev/null +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/composition-graph-zone-utils.ts @@ -0,0 +1,55 @@ +import { PolicyInstance } from "app/models/graph/zones/policy-instance"; +import { ZoneConfig, ZoneInstanceConfig } from "app/models/graph/zones/zone-child"; +import { DynamicComponentService } from "app/ng2/services/dynamic-component.service"; +import { PaletteAnimationComponent } from "app/ng2/components/ui/palette-animation/palette-animation.component"; +import { Point } from "../../../../models"; + +export class CompositionGraphZoneUtils { + + constructor(private dynamicComponentService: DynamicComponentService) {} + + public createCompositionZones(){ + let zones = { + 'policy': new ZoneConfig('Policies', 'P', 'policy', false), + 'group': new ZoneConfig('Groups', 'G', 'group', false) + }; + return zones; + } + + public initPolicyInstances(policyZone:ZoneConfig, policies:Array<PolicyInstance>) { + if(policies && policies.length){ + policyZone.showZone = true; + } + _.forEach(policies, (policy:PolicyInstance) => { + policyZone.instances.push(new ZoneInstanceConfig(policy)); + }); + } + + public addInstanceToZone(zone:ZoneConfig, instance:PolicyInstance){ + zone.instances.push(new ZoneInstanceConfig(instance)); + }; + + private findZoneCoordinates(zoneType):Point{ + let point:Point = new Point(0,0); + let zone = angular.element(document.querySelector('.' + zoneType + '-zone')); + let wrapperZone = zone.offsetParent(); + point.x = zone.prop('offsetLeft') + wrapperZone.prop('offsetLeft'); + point.y = zone.prop('offsetTop') + wrapperZone.prop('offsetTop'); + return point; + } + + public showAnimationToZone = (startPoint:Point, zoneType:string) => { + + let paletteToZoneAnimation = this.dynamicComponentService.createDynamicComponent(PaletteAnimationComponent); + paletteToZoneAnimation.instance.from = startPoint; + paletteToZoneAnimation.instance.to = this.findZoneCoordinates(zoneType); + paletteToZoneAnimation.instance.iconName = zoneType; + paletteToZoneAnimation.instance.runAnimation(); + } + + +} + +CompositionGraphZoneUtils.$inject = [ + 'DynamicComponentService' +];
\ No newline at end of file diff --git a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts index 6a02381902..3a05ce901f 100644 --- a/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts +++ b/catalog-ui/src/app/directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Requirement, CompositionCiLinkBase, CapabilitiesGroup, RequirementsGroup, Match, CompositionCiNodeBase, Component, Capability @@ -142,7 +143,7 @@ export class MatchCapabilitiesRequirementsUtils { let requirementArray:Array<Requirement> = []; _.forEach(_.flatten(_.values(requirements)), (requirement:Requirement)=> { - if (requirement.name !== "dependency" && !MatchCapabilitiesRequirementsUtils.isRequirementFulfilled(fromNodeId, requirement, links)) { + if (requirement.name !== 'dependency' && requirement.parentName !== 'dependency' && !MatchCapabilitiesRequirementsUtils.isRequirementFulfilled(fromNodeId, requirement, links)) { requirementArray.push(requirement); } }); diff --git a/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts b/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts index 127f43b734..c542e9fc95 100644 --- a/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts +++ b/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-graph.directive.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Component, Module, NodesFactory, ComponentInstance } from "app/models"; import { ComponentInstanceFactory } from "app/utils"; import { DeploymentGraphGeneralUtils } from "./deployment-utils/deployment-graph-general-utils"; @@ -133,7 +134,7 @@ export class DeploymentGraph implements ng.IDirective { }); this.initGraphNodes(this._cy, scope.component); //creating instances nodes - this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations); + this.commonGraphUtils.initGraphLinks(this._cy, scope.component.componentInstancesRelations, scope.component.getRelationRequirementCapability.bind(scope.component)); this._cy.collapseAll(); this.registerGraphEvents(); diff --git a/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts b/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts index e481b1d268..3a90115179 100644 --- a/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts +++ b/catalog-ui/src/app/directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {Module} from "app/models"; /** * Created by obarda on 12/21/2016. diff --git a/catalog-ui/src/app/directives/graphs-v2/palette/palette.directive.ts b/catalog-ui/src/app/directives/graphs-v2/palette/palette.directive.ts index 8abf968c82..9b9235248e 100644 --- a/catalog-ui/src/app/directives/graphs-v2/palette/palette.directive.ts +++ b/catalog-ui/src/app/directives/graphs-v2/palette/palette.directive.ts @@ -7,9 +7,9 @@ * 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. @@ -17,7 +17,8 @@ * limitations under the License. * ============LICENSE_END========================================================= */ -import {Component, IAppMenu, LeftPanelModel, NodesFactory, LeftPaletteComponent, CompositionCiNodeBase, ComponentInstance} from "app/models"; +import * as _ from "lodash"; +import {Component, IAppMenu, LeftPanelModel, NodesFactory, LeftPaletteComponent, CompositionCiNodeBase, ComponentInstance, Point} from "app/models"; import {CompositionGraphGeneralUtils} from "../composition-graph/utils/composition-graph-general-utils"; import {EventListenerService} from "app/services"; import {ResourceType, GRAPH_EVENTS, EVENTS, ComponentInstanceFactory, ModalsHandler} from "app/utils"; @@ -25,6 +26,8 @@ import 'sdc-angular-dragdrop'; import {LeftPaletteLoaderService} from "../../../services/components/utils/composition-left-palette-service"; import {Resource} from "app/models/components/resource"; import {ComponentType} from "app/utils/constants"; +import {LeftPaletteMetadataTypes} from "../../../models/components/displayComponent"; + interface IPaletteScope { components:Array<LeftPaletteComponent>; @@ -39,10 +42,15 @@ interface IPaletteScope { ui:any } + addInstanceClick: ()=>void; // added code + onPopupMouseOver: ()=>void // added code + onPopupMouseOut: ()=>void // added code + sectionClick:(section:string)=>void; searchComponents:(searchText:string)=>void; - onMouseOver:(displayComponent:LeftPaletteComponent)=>void; + onMouseOver:(displayComponent:LeftPaletteComponent, elem: HTMLElement)=>void; onMouseOut:(displayComponent:LeftPaletteComponent)=>void; + dragStartCallback:(event:JQueryEventObject, ui, displayComponent:LeftPaletteComponent)=>void; dragStopCallback:()=>void; onDragCallback:(event:JQueryEventObject) => void; @@ -65,8 +73,8 @@ export class Palette implements ng.IDirective { private CompositionGraphGeneralUtils:CompositionGraphGeneralUtils, private EventListenerService:EventListenerService, private sdcMenu:IAppMenu, - private ModalsHandler:ModalsHandler) { - + private ModalsHandler:ModalsHandler + ) { } private fetchingComponentFromServer:boolean = false; @@ -84,7 +92,6 @@ export class Palette implements ng.IDirective { this.nodeHtmlSubstitute = $('<div class="node-substitute"><span></span><img /></div>'); el.append(this.nodeHtmlSubstitute); this.registerEventListenerForLeftPalette(scope); - // this.LeftPaletteLoaderService.loadLeftPanel(scope.currentComponent.componentType); this.initComponents(scope); this.initEvents(scope); @@ -96,31 +103,15 @@ export class Palette implements ng.IDirective { }); }; - private getUpdateLeftPaletteEventName = (component:Component):string => { - switch (component.componentType) { - case ComponentType.SERVICE: - return EVENTS.SERVICE_LEFT_PALETTE_UPDATE_EVENT; - case ComponentType.RESOURCE: - if((<Resource>component).resourceType == ResourceType.PNF){ - return EVENTS.RESOURCE_PNF_LEFT_PALETTE_UPDATE_EVENT; - }else{ - return EVENTS.RESOURCE_LEFT_PALETTE_UPDATE_EVENT; - } - default: - console.log('ERROR: Component type '+ component.componentType + ' is not exists'); - } - }; private registerEventListenerForLeftPalette = (scope:IPaletteScope):void => { - let updateEventName:string = this.getUpdateLeftPaletteEventName(scope.currentComponent); - this.EventListenerService.registerObserverCallback(updateEventName, () => { + this.EventListenerService.registerObserverCallback(EVENTS.LEFT_PALETTE_UPDATE_EVENT, () => { this.updateLeftPanelDisplay(scope); }); }; private unRegisterEventListenerForLeftPalette = (scope:IPaletteScope):void => { - let updateEventName:string = this.getUpdateLeftPaletteEventName(scope.currentComponent); - this.EventListenerService.unRegisterObserver(updateEventName); + this.EventListenerService.unRegisterObserver(EVENTS.LEFT_PALETTE_UPDATE_EVENT); }; private leftPanelResourceFilter(resourcesNotAbstract:Array<LeftPaletteComponent>, resourceFilterTypes:Array<string>):Array<LeftPaletteComponent> { @@ -132,7 +123,7 @@ export class Palette implements ng.IDirective { private initLeftPanel(leftPanelComponents:Array<LeftPaletteComponent>, resourceFilterTypes:Array<string>):LeftPanelModel { let leftPanelModel = new LeftPanelModel(); - + if (resourceFilterTypes && resourceFilterTypes.length) { leftPanelComponents = this.leftPanelResourceFilter(leftPanelComponents, resourceFilterTypes); } @@ -151,10 +142,7 @@ export class Palette implements ng.IDirective { private initEvents(scope:IPaletteScope) { - /** - * - * @param section - */ + scope.sectionClick = (section:string) => { if (section === scope.expandedSection) { scope.expandedSection = ''; @@ -163,20 +151,37 @@ export class Palette implements ng.IDirective { scope.expandedSection = section; }; - scope.onMouseOver = (displayComponent:LeftPaletteComponent) => { - if (scope.isOnDrag) { - return; + scope.onMouseOver = (displayComponent:LeftPaletteComponent, sectionElem: HTMLElement) => { + if (this.isGroupOrPolicy(displayComponent)) { + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_SHOW_POPUP_PANEL, scope.currentComponent, displayComponent, sectionElem); + } else { + if (scope.isOnDrag) { + return; + } + scope.isOnDrag = true; + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, displayComponent); + this.$log.debug('palette::onMouseOver:: fired'); } - scope.isOnDrag = true; - this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, displayComponent); - this.$log.debug('palette::onMouseOver:: fired'); }; - scope.onMouseOut = () => { - scope.isOnDrag = false; - this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT); + scope.onMouseOut = (displayComponent:LeftPaletteComponent) => { + if(this.isGroupOrPolicy(displayComponent)) { + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HIDE_POPUP_PANEL); + } else { + scope.isOnDrag = false; + this.EventListenerService.notifyObservers(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT); + } + }; + } + + private isGroupOrPolicy(component:LeftPaletteComponent): boolean { + if(component && + (component.categoryType === LeftPaletteMetadataTypes.Group || + component.categoryType === LeftPaletteMetadataTypes.Policy)) { + return true; } + return false; } private initComponents(scope:IPaletteScope) { @@ -193,10 +198,10 @@ export class Palette implements ng.IDirective { let entityType:string = scope.currentComponent.componentType.toLowerCase(); let resourceFilterTypes:Array<string> = this.sdcConfig.resourceTypesFilter[entityType]; scope.components = this.LeftPaletteLoaderService.getLeftPanelComponentsForDisplay(scope.currentComponent); - //remove the container component from the list + //remove the container component from the list let componentTempToDisplay = angular.copy(scope.components); - componentTempToDisplay = _.remove(componentTempToDisplay, function (component) { - return component.component.invariantUUID !== scope.currentComponent.invariantUUID; + componentTempToDisplay = _.remove(componentTempToDisplay, function (leftPalettecomponent) { + return leftPalettecomponent.invariantUUID !== scope.currentComponent.invariantUUID; }); scope.model = this.initLeftPanel(componentTempToDisplay, resourceFilterTypes); scope.displaySortedCategories = angular.copy(scope.model.sortedCategories); @@ -224,7 +229,7 @@ export class Palette implements ng.IDirective { private initDragEvents(scope:IPaletteScope) { scope.dragStartCallback = (event:IDragDropEvent, ui, displayComponent:LeftPaletteComponent):void => { - if (scope.isLoading || !scope.isDragable || scope.isViewOnly) { + if (scope.isLoading || !scope.isDragable || scope.isViewOnly || this.isGroupOrPolicy(displayComponent)) { return; } @@ -302,7 +307,8 @@ export class Palette implements ng.IDirective { CompositionGraphGeneralUtils, EventListenerService, sdcMenu, - ModalsHandler) => { + ModalsHandler + ) => { return new Palette($log, LeftPaletteLoaderService, sdcConfig, @@ -312,7 +318,8 @@ export class Palette implements ng.IDirective { CompositionGraphGeneralUtils, EventListenerService, sdcMenu, - ModalsHandler); + ModalsHandler + ); }; } diff --git a/catalog-ui/src/app/directives/graphs-v2/palette/palette.html b/catalog-ui/src/app/directives/graphs-v2/palette/palette.html index 7f9bfd1e0d..a8139e3140 100644 --- a/catalog-ui/src/app/directives/graphs-v2/palette/palette.html +++ b/catalog-ui/src/app/directives/graphs-v2/palette/palette.html @@ -24,9 +24,9 @@ </div> <div class="i-sdc-designer-leftbar-section-content-item" data-ng-class="{'default-pointer': isViewOnly}" - data-ng-mouseover="!isViewOnly && onMouseOver(component)" - data-ng-mouseleave="!isViewOnly && onMouseOut()" - data-drag="{{!isViewOnly}}" + data-ng-mouseover="!isViewOnly && onMouseOver(component, $event.currentTarget)" + data-ng-mouseleave="!isViewOnly && onMouseOut(component)" + data-drag="!isViewOnly" data-jqyoui-options="{revert: 'invalid', helper:setElementTemplate, appendTo:'body', cursorAt: {left:38, top: 38}, cursor:'move'}" jqyoui-draggable="{index:{{$index}},animate:true,onStart:'dragStartCallback(component)',onStop:'dragStopCallback()', onDrag:'onDragCallback()'}" data-ng-repeat="component in components | orderBy: 'displayName' track by $index" @@ -40,14 +40,16 @@ </div> </div> <div class="i-sdc-designer-leftbar-section-content-item-info"> - <span class="i-sdc-designer-leftbar-section-content-item-info-title" - uib-tooltip="{{component.displayName}}" tooltip-class="uib-custom-tooltip" - tooltip-placement="bottom" tooltip-popup-delay="700"> - {{component.displayName}}</span> + <span class="i-sdc-designer-leftbar-section-content-item-info-title" + uib-tooltip="{{component.displayName}}" tooltip-class="uib-custom-tooltip" + tooltip-placement="bottom" tooltip-popup-delay="700"> + {{component.displayName}}</span> <div class="i-sdc-designer-leftbar-section-content-item-info-text"> V.{{component.version}} </div> - <div class="i-sdc-designer-leftbar-section-content-item-info-text"> Type: + <div class="i-sdc-designer-leftbar-section-content-item-info-text" + uib-tooltip="{{component.type}}" tooltip-class="uib-custom-tooltip" + tooltip-placement="top" tooltip-popup-delay="700"> Type: {{component.componentSubType}} </div> </div> diff --git a/catalog-ui/src/app/directives/graphs-v2/relation-menu/relation-menu.ts b/catalog-ui/src/app/directives/graphs-v2/relation-menu/relation-menu.ts index f73e855c0e..78a269ead1 100644 --- a/catalog-ui/src/app/directives/graphs-v2/relation-menu/relation-menu.ts +++ b/catalog-ui/src/app/directives/graphs-v2/relation-menu/relation-menu.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ 'use strict' +import * as _ from "lodash"; import {Match, ConnectRelationModel} from "app/models"; import {Component} from "../../../models/components/component"; diff --git a/catalog-ui/src/app/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts b/catalog-ui/src/app/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts index 476f86d8a6..eed209d438 100644 --- a/catalog-ui/src/app/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts +++ b/catalog-ui/src/app/directives/property-types/data-type-fields-structure/data-type-fields-structure.ts @@ -22,6 +22,7 @@ * Created by obarda on 1/27/2016. */ 'use strict'; +import * as _ from "lodash"; import {DataTypesService} from "app/services"; import { ValidationUtils } from "app/utils"; import { DataTypePropertyModel } from "app/models/data-type-properties"; diff --git a/catalog-ui/src/app/directives/select-property-types/select-data-type-fields-structure/select-data-type-fields-structure.ts b/catalog-ui/src/app/directives/select-property-types/select-data-type-fields-structure/select-data-type-fields-structure.ts index a8bcf3f155..f5e9a36041 100644 --- a/catalog-ui/src/app/directives/select-property-types/select-data-type-fields-structure/select-data-type-fields-structure.ts +++ b/catalog-ui/src/app/directives/select-property-types/select-data-type-fields-structure/select-data-type-fields-structure.ts @@ -22,6 +22,7 @@ * Created by obarda on 1/27/2016. */ 'use strict'; +import * as _ from "lodash"; import {ValidationUtils} from "app/utils"; import { DataTypesService } from "app/services"; import { DataTypePropertyModel } from "app/models/data-type-properties"; diff --git a/catalog-ui/src/app/directives/structure-tree/structure-tree-directive.ts b/catalog-ui/src/app/directives/structure-tree/structure-tree-directive.ts index 4f80ae86c0..59fa700dc9 100644 --- a/catalog-ui/src/app/directives/structure-tree/structure-tree-directive.ts +++ b/catalog-ui/src/app/directives/structure-tree/structure-tree-directive.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ 'use strict'; +import * as _ from "lodash"; import {RelationshipModel, Component, ComponentInstance} from "app/models"; import {Dictionary} from "app/utils"; diff --git a/catalog-ui/src/app/filters/category-type-filter.ts b/catalog-ui/src/app/filters/category-type-filter.ts index 5c89277bb8..763b4a94e2 100644 --- a/catalog-ui/src/app/filters/category-type-filter.ts +++ b/catalog-ui/src/app/filters/category-type-filter.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {ComponentType} from "../utils/constants"; import {CacheService} from "../services/cache-service"; export class CategoryTypeFilter { diff --git a/catalog-ui/src/app/filters/entity-filter.ts b/catalog-ui/src/app/filters/entity-filter.ts index 6d04bc3c41..ccac10f802 100644 --- a/catalog-ui/src/app/filters/entity-filter.ts +++ b/catalog-ui/src/app/filters/entity-filter.ts @@ -19,6 +19,8 @@ */ import {Component, Resource} from "../models"; +import {ComponentType} from "../utils/constants"; + export class EntityFilter { constructor() { @@ -56,11 +58,14 @@ export class EntityFilter { if (filter.selectedCategoriesModel && filter.selectedCategoriesModel.length > 0) { let filteredCategories = []; angular.forEach(filteredComponents, (component:Component):void => { - if (component.categories && filter.selectedCategoriesModel.indexOf(component.categories[0].uniqueId) !== -1) { - filteredCategories.push(component); - } else if (component.categories && component.categories[0].subcategories && filter.selectedCategoriesModel.indexOf(component.categories[0].subcategories[0].uniqueId) !== -1) { - filteredCategories.push(component); - } else if (component.categories && component.categories[0].subcategories && component.categories[0].subcategories[0].groupings && filter.selectedCategoriesModel.indexOf(component.categories[0].subcategories[0].groupings[0].uniqueId) !== -1) { + let componentCategory = component.categoryNormalizedName + + ((component.subCategoryNormalizedName) ? '.' + component.subCategoryNormalizedName : ''); + if (component.componentType === ComponentType.RESOURCE) { + componentCategory = 'resourceNewCategory.' + componentCategory; + } else if (component.componentType === ComponentType.SERVICE) { + componentCategory = 'serviceNewCategory.' + componentCategory; + } + if (filter.selectedCategoriesModel.indexOf(componentCategory) !== -1) { filteredCategories.push(component); } }); diff --git a/catalog-ui/src/app/filters/resource-name-filter.ts b/catalog-ui/src/app/filters/resource-name-filter.ts index 97de5a2e76..5aa114d1b4 100644 --- a/catalog-ui/src/app/filters/resource-name-filter.ts +++ b/catalog-ui/src/app/filters/resource-name-filter.ts @@ -18,6 +18,8 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; + export class ResourceNameFilter { diff --git a/catalog-ui/src/app/models/app-config.ts b/catalog-ui/src/app/models/app-config.ts index 1f6682f666..54c059afb3 100644 --- a/catalog-ui/src/app/models/app-config.ts +++ b/catalog-ui/src/app/models/app-config.ts @@ -84,6 +84,7 @@ export interface IApi { GET_onboarding:string; GET_component_from_csar_uuid:string; kibana:string; + services_api_root:string; } export interface ILogConfig { diff --git a/catalog-ui/src/app/models/artifacts.ts b/catalog-ui/src/app/models/artifacts.ts index 0aa07d11ed..959480b0f0 100644 --- a/catalog-ui/src/app/models/artifacts.ts +++ b/catalog-ui/src/app/models/artifacts.ts @@ -20,6 +20,7 @@ 'use strict'; +import * as _ from "lodash"; import {ArtifactType} from './../utils'; import {HeatParameterModel} from "./heat-parameters"; diff --git a/catalog-ui/src/app/models/attributes.ts b/catalog-ui/src/app/models/attributes.ts index 9e2dc98855..9e5828ae69 100644 --- a/catalog-ui/src/app/models/attributes.ts +++ b/catalog-ui/src/app/models/attributes.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {SchemaAttributeGroupModel, SchemaAttribute} from "./schema-attribute"; import {SchemaPropertyGroupModel, SchemaProperty} from "./aschema-property"; diff --git a/catalog-ui/src/app/models/capability.ts b/catalog-ui/src/app/models/capability.ts index 2f2e3a8de6..74994b155b 100644 --- a/catalog-ui/src/app/models/capability.ts +++ b/catalog-ui/src/app/models/capability.ts @@ -22,6 +22,7 @@ * Created by obarda on 4/20/2016. */ 'use strict'; +import * as _ from "lodash"; import {PropertyModel} from "./properties"; import {Requirement} from "./requirement"; diff --git a/catalog-ui/src/app/models/components/component.ts b/catalog-ui/src/app/models/components/component.ts index daa4a19e19..b7f57227ca 100644 --- a/catalog-ui/src/app/models/components/component.ts +++ b/catalog-ui/src/app/models/components/component.ts @@ -20,6 +20,7 @@ 'use strict'; +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"; @@ -31,6 +32,9 @@ import {ArtifactGroupType} from "../../utils/constants"; import {ComponentMetadata} from "../component-metadata"; import {Capability} from "../capability"; import {Requirement} from "../requirement"; +import {Relationship} from "../graph/relationship"; +import { PolicyInstance } from "app/models/graph/zones/policy-instance"; + // import {} export interface IComponent { @@ -57,7 +61,7 @@ export interface IComponent { //Property API addOrUpdateProperty(property:PropertyModel):ng.IPromise<PropertyModel>; deleteProperty(propertyId:string):ng.IPromise<PropertyModel>; - updateInstanceProperty(property:PropertyModel):ng.IPromise<PropertyModel>; + updateInstanceProperties(componentInstanceId:string, properties:PropertyModel[]):ng.IPromise<PropertyModel[]>; //Attribute API deleteAttribute(attributeId:string):ng.IPromise<AttributeModel>; @@ -73,6 +77,7 @@ export interface IComponent { addOrUpdateInstanceArtifact(artifact:ArtifactModel):ng.IPromise<ArtifactModel>; deleteInstanceArtifact(artifactId:string, artifactLabel:string):ng.IPromise<ArtifactModel>; uploadInstanceEnvFile(artifact:ArtifactModel):ng.IPromise<ArtifactModel>; + checkComponentInstanceVersionChange(componentUid:string):ng.IPromise<any>; changeComponentInstanceVersion(componentUid:string):ng.IPromise<Component>; updateComponentInstance(componentInstance:ComponentInstance):ng.IPromise<ComponentInstance>; updateMultipleComponentInstances(instances:Array<ComponentInstance>):ng.IPromise<Array<ComponentInstance>>; @@ -131,6 +136,8 @@ export abstract class Component implements IComponent { public toscaArtifacts:ArtifactGroupModel; public distributionStatus:string; public categories:Array<IMainCategory>; + public categoryNormalizedName: string; + public subCategoryNormalizedName: string; public componentInstancesProperties:PropertiesGroup; public componentInstancesAttributes:AttributesGroup; public componentInstancesRelations:Array<RelationshipModel>; @@ -149,6 +156,7 @@ export abstract class Component implements IComponent { public normalizedName:string; public systemName:string; public projectCode:string; + public policies:Array<PolicyInstance>; public groups:Array<Module>; //custom properties public componentService:IComponentService; @@ -172,6 +180,8 @@ export abstract class Component implements IComponent { this.toscaArtifacts = new ArtifactGroupModel(component.toscaArtifacts); this.contactId = component.contactId; this.categories = component.categories; + this.categoryNormalizedName = component.categoryNormalizedName; + this.subCategoryNormalizedName = component.subCategoryNormalizedName; this.creatorUserId = component.creatorUserId; this.creationDate = component.creationDate; this.creatorFullName = component.creatorFullName; @@ -423,23 +433,25 @@ export abstract class Component implements IComponent { return deferred.promise; }; - public updateInstanceProperty = (property:PropertyModel):ng.IPromise<PropertyModel> => { - let deferred = this.$q.defer(); - let onSuccess = (newProperty:PropertyModel):void => { - // find exist instance property in parent component for update the new value ( find bu uniqueId & path) - let existProperty:PropertyModel = <PropertyModel>_.find(this.componentInstancesProperties[newProperty.resourceInstanceUniqueId], { - uniqueId: newProperty.uniqueId, - path: newProperty.path + public updateInstanceProperties = (componentInstanceId:string, properties:PropertyModel[]):ng.IPromise<PropertyModel[]> => { + let deferred = this.$q.defer<PropertyModel[]>(); + let onSuccess = (newProperties:PropertyModel[]):void => { + newProperties.forEach((newProperty) => { + // find exist instance property in parent component for update the new value ( find bu uniqueId & path) + let existProperty: PropertyModel = <PropertyModel>_.find(this.componentInstancesProperties[newProperty.resourceInstanceUniqueId], { + uniqueId: newProperty.uniqueId, + path: newProperty.path + }); + let index = this.componentInstancesProperties[newProperty.resourceInstanceUniqueId].indexOf(existProperty); + this.componentInstancesProperties[newProperty.resourceInstanceUniqueId][index] = newProperty; }); - let index = this.componentInstancesProperties[newProperty.resourceInstanceUniqueId].indexOf(existProperty); - this.componentInstancesProperties[newProperty.resourceInstanceUniqueId][index] = newProperty; - deferred.resolve(newProperty); + deferred.resolve(newProperties); }; let onFailed = (error:any):void => { console.log('Failed to update property value'); deferred.reject(error); }; - this.componentService.updateInstanceProperty(this.uniqueId, property).then(onSuccess, onFailed); + this.componentService.updateInstanceProperties(this.uniqueId, componentInstanceId, properties).then(onSuccess, onFailed); return deferred.promise; }; @@ -534,6 +546,10 @@ export abstract class Component implements IComponent { return deferred.promise; }; + public checkComponentInstanceVersionChange = (componentUid:string):ng.IPromise<any> => { + return this.componentService.checkResourceInstanceVersionChange(this.uniqueId, this.selectedInstance.uniqueId, componentUid); + }; + public createComponentInstance = (componentInstance:ComponentInstance):ng.IPromise<ComponentInstance> => { let deferred = this.$q.defer(); let onSuccess = (instance:ComponentInstance):void => { @@ -713,6 +729,33 @@ export abstract class Component implements IComponent { return deferred.promise; }; + public getRelationRequirementCapability(relationship: Relationship, sourceNode:ComponentInstance, targetNode:ComponentInstance): Promise<{requirement:Requirement, capability:Capability}> { + // try find the requirement and capability in the source and target component instances: + let capability:Capability = targetNode.findCapability(undefined, + relationship.relation.capabilityUid, + relationship.relation.capabilityOwnerId, + relationship.relation.capability); + let requirement:Requirement = sourceNode.findRequirement(undefined, + relationship.relation.requirementUid, + relationship.relation.requirementOwnerId, + relationship.relation.requirement); + + return new Promise<{requirement:Requirement, capability:Capability}>((resolve, reject) => { + if (capability && requirement) { + resolve({capability, requirement}); + } + else { + // if requirement and/or capability is missing, then fetch the full relation with its requirement and capability: + this.fetchRelation(relationship.relation.id).then((fetchedRelation) => { + resolve({ + capability: capability || fetchedRelation.relationships[0].capability, + requirement: requirement || fetchedRelation.relationships[0].requirement + }); + }, reject); + } + }); + } + public updateRequirementsCapabilities = ():ng.IPromise<any> => { let deferred = this.$q.defer(); let onSuccess = (response:any):void => { diff --git a/catalog-ui/src/app/models/components/displayComponent.ts b/catalog-ui/src/app/models/components/displayComponent.ts index 3a9cbfdaf3..c89490306c 100644 --- a/catalog-ui/src/app/models/components/displayComponent.ts +++ b/catalog-ui/src/app/models/components/displayComponent.ts @@ -7,9 +7,9 @@ * 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. @@ -22,14 +22,23 @@ */ 'use strict'; +import * as _ from "lodash"; import {ComponentType} from "../../utils/constants"; import {ComponentMetadata} from "../component-metadata"; +import {PolicyMetadata} from "../policy-metadata"; +import {GroupMetadata} from "../group-metadata"; import {RequirementsGroup} from "../requirement"; import {CapabilitiesGroup} from "../capability"; -export class LeftPaletteComponent { +export enum LeftPaletteMetadataTypes { + Component, + Group, + Policy +} +export class LeftPaletteComponent { uniqueId:string; + type:string; displayName:string; version:string; mainCategory:string; @@ -48,10 +57,33 @@ export class LeftPaletteComponent { componentType:string; systemName:string; + invariantUUID:string; + capabilities:CapabilitiesGroup; requirements:RequirementsGroup; - constructor(public component:ComponentMetadata) { + categoryType:LeftPaletteMetadataTypes; + + constructor(metadataType: LeftPaletteMetadataTypes, item: ComponentMetadata | PolicyMetadata) { + if (metadataType === LeftPaletteMetadataTypes.Policy) { + this.initPolicy(item as PolicyMetadata); + return; + } + + if (metadataType === LeftPaletteMetadataTypes.Group) { + this.initGroup(item as GroupMetadata); + return; + } + + if (metadataType === LeftPaletteMetadataTypes.Component) { + this.initComponent(item as ComponentMetadata); + return; + } + } + + private initComponent(component:ComponentMetadata): void { + this.categoryType = LeftPaletteMetadataTypes.Component; + this.icon = component.icon; this.version = component.version; this.uniqueId = component.uniqueId; @@ -61,6 +93,7 @@ export class LeftPaletteComponent { this.allVersions = component.allVersions; this.componentType = component.componentType; this.systemName = component.systemName; + this.invariantUUID = component.invariantUUID; if (component.categories && component.categories[0] && component.categories[0].subcategories && component.categories[0].subcategories[0]) { this.mainCategory = component.categories[0].name; @@ -71,6 +104,7 @@ export class LeftPaletteComponent { } this.componentSubType = component.resourceType ? component.resourceType: 'SERVICE'; + this.initDisplayName(component.name); this.searchFilterTerms = (this.displayName + ' ' + component.description + ' ' + component.tags.join(' ')).toLowerCase() + ' ' + component.version; this.initIconSprite(component.icon); @@ -80,6 +114,38 @@ export class LeftPaletteComponent { } } + private initGroup(group:GroupMetadata): void { + this.categoryType = LeftPaletteMetadataTypes.Group; + + this.uniqueId = group.uniqueId; + this.displayName = group.type; + this.mainCategory = "Groups"; + this.subCategory = "Groups"; + this.iconClass = "sprite-group-icons group"; + this.version = group.version; + + this.type = group.type; + this.componentSubType = 'GROUP'; + + this.searchFilterTerms = this.displayName + ' ' + group.description + ' ' + group.version; + } + + private initPolicy(policy:PolicyMetadata): void { + this.categoryType = LeftPaletteMetadataTypes.Policy; + + this.uniqueId = policy.uniqueId; + this.displayName = policy.type; + this.mainCategory = "Policies"; + this.subCategory = "Policies"; + this.iconClass = "sprite-policy-icons policy"; + this.version = policy.version; + + this.type = policy.type; + this.componentSubType = 'POLICY'; + + this.searchFilterTerms = this.displayName + ' ' + policy.description + ' ' + policy.version; + } + public initDisplayName = (name:string):void => { let newName = _.last(_.last(_.last(_.last(_.last(_.last(_.last(_.last(name.split('tosca.nodes.')) diff --git a/catalog-ui/src/app/models/components/resource.ts b/catalog-ui/src/app/models/components/resource.ts index cd839786c5..5bd80e3b72 100644 --- a/catalog-ui/src/app/models/components/resource.ts +++ b/catalog-ui/src/app/models/components/resource.ts @@ -22,6 +22,7 @@ * Created by obarda on 2/3/2016. */ 'use strict'; +import * as _ from "lodash"; import {InstancesInputsOrPropertiesMapData} from "../instance-inputs-properties-map"; import {PropertyModel} from "../properties"; import {DisplayModule} from "../modules/base-module"; @@ -91,7 +92,7 @@ export class Resource extends Component { }; public isComplex = ():boolean => { - return this.resourceType === ResourceType.VF || this.resourceType === ResourceType.PNF || this.resourceType === ResourceType.CVFC; + return this.resourceType === ResourceType.VF || this.resourceType === ResourceType.PNF || this.resourceType === ResourceType.CVFC || this.resourceType === ResourceType.CR; }; public isVl = ():boolean => { diff --git a/catalog-ui/src/app/models/components/service.ts b/catalog-ui/src/app/models/components/service.ts index ce6921b1bb..439925f062 100644 --- a/catalog-ui/src/app/models/components/service.ts +++ b/catalog-ui/src/app/models/components/service.ts @@ -22,11 +22,13 @@ * Created by obarda on 2/4/2016. */ 'use strict'; +import * as _ from "lodash"; import {IServiceService} from "../../services/components/service-service"; import {Component, PropertyModel, DisplayModule, InputsAndProperties, InputModel, InstancesInputsOrPropertiesMapData, InstancesInputsPropertiesMap, Distribution, DistributionComponent, ArtifactGroupModel} from "../../models"; import {ArtifactGroupType} from "../../utils/constants"; import {ComponentMetadata} from "../component-metadata"; +import {ForwardingPath} from "app/models/forwarding-path"; export class Service extends Component { @@ -37,6 +39,7 @@ export class Service extends Component { public serviceType:string; public serviceRole:string; public environmentContext:string; + public forwardingPaths:{ [key:string]:ForwardingPath } = {}; constructor(componentService:IServiceService, $q:ng.IQService, component?:Service) { super(componentService, $q, component); diff --git a/catalog-ui/src/app/models/componentsInstances/componentInstance.ts b/catalog-ui/src/app/models/componentsInstances/componentInstance.ts index 59521ccfc8..e8444b3c54 100644 --- a/catalog-ui/src/app/models/componentsInstances/componentInstance.ts +++ b/catalog-ui/src/app/models/componentsInstances/componentInstance.ts @@ -22,6 +22,7 @@ * Created by obarda on 2/4/2016. */ 'use strict'; +import * as _ from "lodash"; import {ArtifactGroupModel, CapabilitiesGroup,RequirementsGroup, PropertyModel, InputModel, Module} from "../../models"; import {ResourceType,ComponentType} from "../../utils/constants"; import {Capability} from "../capability"; @@ -104,7 +105,7 @@ export class ComponentInstance { }; public isComplex = () : boolean => { - return this.originType === ResourceType.VF || this.originType === ResourceType.PNF || this.originType === ResourceType.CVFC ; + return this.originType === ResourceType.VF || this.originType === ResourceType.PNF || this.originType === ResourceType.CVFC || this.originType === ResourceType.CR ; } public isServiceProxy = () :boolean => { diff --git a/catalog-ui/src/app/models/forwarding-path-link.ts b/catalog-ui/src/app/models/forwarding-path-link.ts new file mode 100644 index 0000000000..e09704d34f --- /dev/null +++ b/catalog-ui/src/app/models/forwarding-path-link.ts @@ -0,0 +1,20 @@ +export class ForwardingPathLink { + public ownerId: string; + public fromNode:string; + public fromCP:string; + public toNode:string; + public toCP:string; + public toCPOriginId:string; + public fromCPOriginId:string; + + + constructor(fromNode:string, fromCP:string, toNode:string, toCP:string, fromCPOriginId:string, toCPOriginId:string) { + this.fromCP = fromCP; + this.fromNode = fromNode; + this.toCP = toCP; + this.toNode = toNode; + this.fromCPOriginId = fromCPOriginId; + this.toCPOriginId = toCPOriginId; + } + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/models/forwarding-path.ts b/catalog-ui/src/app/models/forwarding-path.ts new file mode 100644 index 0000000000..2158dfe479 --- /dev/null +++ b/catalog-ui/src/app/models/forwarding-path.ts @@ -0,0 +1,30 @@ +import {PathElements} from './path-elements'; +import {ForwardingPathLink} from "./forwarding-path-link"; + +export class ForwardingPath { + public name:string; + public destinationPortNumber:string; + public protocol:string; + public uniqueId:string; + public ownerId: string; + public pathElements: PathElements; + + addPathLink(fromNode:string, fromCP:string, toNode:string, toCP:string, fromCPOriginId: string, toCPOriginId: string) { + if (!this.pathElements) { + this.pathElements = new PathElements(); + } + this.pathElements.listToscaDataDefinition[this.pathElements.listToscaDataDefinition.length] = new ForwardingPathLink(fromNode, fromCP, toNode, toCP, fromCPOriginId, toCPOriginId); + } + + deserialize(response:any) { + this.name = response.name; + this.destinationPortNumber = response.destinationPortNumber; + this.protocol = response.protocol; + if (response.pathElements && response.pathElements.listToscaDataDefinition) { + let list = response.pathElements.listToscaDataDefinition; + for (let i = 0; i < list.length; i++) { + this.addPathLink(list[i].fromNode, list[i].fromCP, list[i].toNode, list[i].toCP, list[i].fromCPOriginId, list[i].toCPOriginId); + } + } + } +}; diff --git a/catalog-ui/src/app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link.ts b/catalog-ui/src/app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link.ts new file mode 100644 index 0000000000..7aa4650e4e --- /dev/null +++ b/catalog-ui/src/app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link.ts @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {CommonLinkBase} from "../common-base-link"; +import {ForwardingPathLink} from "app/models/forwarding-path-link"; + +export class CompositionCiServicePathLink extends CommonLinkBase { + static LINK_TYPE = 'service-path-link'; + forwardingPathLink:ForwardingPathLink; + pathId:string; + pathName:string; + + constructor(forwardingPathLink:ForwardingPathLink) { + super(); + this.type = CompositionCiServicePathLink.LINK_TYPE; + this.classes = 'service-path-link'; + this.pathId = ''; + this.pathName = ''; + this.forwardingPathLink = forwardingPathLink; + } + + +} diff --git a/catalog-ui/src/app/models/graph/graph-links/links-factory.ts b/catalog-ui/src/app/models/graph/graph-links/links-factory.ts index 88c5323330..cc037a7e22 100644 --- a/catalog-ui/src/app/models/graph/graph-links/links-factory.ts +++ b/catalog-ui/src/app/models/graph/graph-links/links-factory.ts @@ -21,6 +21,7 @@ * Created by obarda on 5/1/2016. */ 'use strict'; +import * as _ from "lodash"; import {RelationshipModel, Relationship, CompositionCiLinkBase, CompositionCiNodeBase, LinkUcpeHost, CompositionCiUcpeLink, CompositionCiVlUcpeLink, CompositionCiSimpleLink, ModuleCiLinkBase, ModuleCiVlLink, CompositionCiVLink} from "../../../models"; diff --git a/catalog-ui/src/app/models/graph/match-relation.ts b/catalog-ui/src/app/models/graph/match-relation.ts index 8d139d6405..2a1b2146b9 100644 --- a/catalog-ui/src/app/models/graph/match-relation.ts +++ b/catalog-ui/src/app/models/graph/match-relation.ts @@ -29,7 +29,6 @@ export class Match { isFromTo:boolean; fromNode:string; toNode:string; - capabilityProperties:Array<PropertyModel>; // use this to store the capability properties, since there are times the capability itself is not available (when fulfilled). private _relationship:Relationship; constructor(requirement:Requirement, capability:Capability, isFromTo:boolean, fromNode:string, toNode:string) { diff --git a/catalog-ui/src/app/models/graph/nodes-and-links-map.ts b/catalog-ui/src/app/models/graph/nodes-and-links-map.ts new file mode 100644 index 0000000000..8c8d6cc944 --- /dev/null +++ b/catalog-ui/src/app/models/graph/nodes-and-links-map.ts @@ -0,0 +1,7 @@ +export class ServicePathMapItem { + constructor(public data: MapItemData, public id: string) {} +} + +export class MapItemData { + constructor(public name:string, public id: string, public ownerId?: string, public options?: Array<ServicePathMapItem>) {} +} diff --git a/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts b/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts index 29cd9256d9..eded75d5da 100644 --- a/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts +++ b/catalog-ui/src/app/models/graph/nodes/composition-graph-nodes/composition-ci-node-vl.ts @@ -17,6 +17,7 @@ * limitations under the License. * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {ComponentInstance} from "../../../componentsInstances/componentInstance"; import {ImageCreatorService} from "../../../../directives/graphs-v2/image-creator/image-creator.service"; import {CompositionCiNodeBase} from "./composition-ci-node-base"; diff --git a/catalog-ui/src/app/models/graph/relationship.ts b/catalog-ui/src/app/models/graph/relationship.ts index 57ff45ef00..8d56272fa0 100644 --- a/catalog-ui/src/app/models/graph/relationship.ts +++ b/catalog-ui/src/app/models/graph/relationship.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ 'use strict'; +import * as _ from "lodash"; import {Capability} from "../capability"; import {Requirement} from "../requirement"; diff --git a/catalog-ui/src/app/models/graph/zones/policy-instance.ts b/catalog-ui/src/app/models/graph/zones/policy-instance.ts new file mode 100644 index 0000000000..63136e6e21 --- /dev/null +++ b/catalog-ui/src/app/models/graph/zones/policy-instance.ts @@ -0,0 +1,43 @@ +import { PropertyModel } from "app/models"; +import { CommonUtils } from "app/utils"; + + +export class PolicyInstance { + componentName:string; + description:string; + empty:boolean; + invariantName:string; + invariantUUID:string; + isFromCsar:boolean; + + name:string; + normalizedName:string; + policyTypeName:string; + policyTypeUid:string; + policyUUID:string; + properties:Array<PropertyModel>; + targets:Array<string>; + uniqueId:string; + version:string; + + constructor(policy?:PolicyInstance) { + this.componentName = policy.componentName; + this.description = policy.description; + this.empty = policy.empty; + this.invariantName = policy.invariantName; + this.invariantUUID = policy.invariantUUID; + this.isFromCsar = policy.isFromCsar; + + this.name = policy.name; + this.normalizedName =policy.normalizedName; + this.policyTypeName = policy.policyTypeName; + this.policyTypeUid = policy.policyTypeUid; + this.policyUUID = policy.policyUUID; + this.properties = CommonUtils.initProperties(policy.properties); + this.targets = policy.targets; + this.uniqueId = policy.uniqueId; + this.version = policy.version; + + } + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/models/graph/zones/zone-child.ts b/catalog-ui/src/app/models/graph/zones/zone-child.ts new file mode 100644 index 0000000000..d6d7198222 --- /dev/null +++ b/catalog-ui/src/app/models/graph/zones/zone-child.ts @@ -0,0 +1,48 @@ +import { Type, Component } from "@angular/core"; +import { PolicyInstance } from "app/models/graph/zones/policy-instance"; + +export class ZoneConfig { + title:string; + defaultIconText:string; + type:string; 'policy|group'; + tagModeId:string; + instances:Array<ZoneInstanceConfig>; + showZone:boolean; + + + constructor (title:string, defaultText:string, type:string, showZone:boolean) { + this.title = title; + this.defaultIconText = defaultText; + this.type = type; + this.tagModeId = this.type + "-tagging"; + this.instances = []; + this.showZone = showZone; + } +} + +export class ZoneInstanceConfig { + + name:string; + assignments:Array<string>; //targets or members + instanceData:PolicyInstance; // | GroupInstance; + mode:ZoneInstanceMode; + + constructor(instance:PolicyInstance) { /* | GroupInstance */ + + this.name = instance.name; + this.instanceData = instance; + this.mode = ZoneInstanceMode.NONE; + + if(instance instanceof PolicyInstance) { + this.assignments = instance.targets; + } + } + +} + +export enum ZoneInstanceMode { + NONE, + HOVER, + SELECTED, + TAG +}
\ No newline at end of file diff --git a/catalog-ui/src/app/models/group-metadata.ts b/catalog-ui/src/app/models/group-metadata.ts new file mode 100644 index 0000000000..ecd6e3e91c --- /dev/null +++ b/catalog-ui/src/app/models/group-metadata.ts @@ -0,0 +1,33 @@ +export class GroupMetadata { + public uniqueId: string; + public type: string; + public version: string; + public description: string; + public creationTime: number; + public modificationTime: number; + public highestVersion: boolean; + public empty: boolean; + + deserialize (response): GroupMetadata { + this.uniqueId = response.uniqueId; + this.type = response.type; + this.version = response.version; + this.description = response.description; + this.creationTime = response.creationTime; + this.modificationTime = response.modificationTime; + this.highestVersion = response.highestVersion; + this.empty = response.empty; + + return this; + } +} + +export interface GroupTpes { + groupTypes: Array<GroupMetadata>; + excludeMapping: ExcludedGroupTypes; +} + +export interface ExcludedGroupTypes { + componentType: string; + excludedGroupTypes: Array<string>; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/models/instance-inputs-properties-map.ts b/catalog-ui/src/app/models/instance-inputs-properties-map.ts index 650a6d7b51..fdc6f926e3 100644 --- a/catalog-ui/src/app/models/instance-inputs-properties-map.ts +++ b/catalog-ui/src/app/models/instance-inputs-properties-map.ts @@ -22,6 +22,7 @@ * Created by obarda on 9/12/2016. */ 'use strict'; +import * as _ from "lodash"; import {InputPropertyBase} from "./input-property-base"; import {PropertyModel} from "./properties"; import {InputModel} from "./inputs"; diff --git a/catalog-ui/src/app/models/member.ts b/catalog-ui/src/app/models/member.ts index fff523ea5e..eeb292eeaa 100644 --- a/catalog-ui/src/app/models/member.ts +++ b/catalog-ui/src/app/models/member.ts @@ -22,6 +22,7 @@ * Created by obarda on 8/2/2016. */ 'use strict'; +import * as _ from "lodash"; export class Members { diff --git a/catalog-ui/src/app/models/modal.ts b/catalog-ui/src/app/models/modal.ts index b7bdf251fe..f0a632727a 100644 --- a/catalog-ui/src/app/models/modal.ts +++ b/catalog-ui/src/app/models/modal.ts @@ -6,13 +6,15 @@ export class ModalModel { content: any; buttons: Array<ButtonModel>; type: string; 'standard|error|alert' + isMovable: boolean; - constructor(size?: string, title?: string, content?: any, buttons?: Array<ButtonModel>, type?:string) { + constructor(size?: string, title?: string, content?: any, buttons?: Array<ButtonModel>, type?: string, isMovable?: boolean) { this.size = size; this.title = title; this.content = content; this.buttons = buttons; this.type = type || 'standard'; + this.isMovable = !!isMovable; } } diff --git a/catalog-ui/src/app/models/modules/base-module.ts b/catalog-ui/src/app/models/modules/base-module.ts index 53a28f076e..63f4cc7103 100644 --- a/catalog-ui/src/app/models/modules/base-module.ts +++ b/catalog-ui/src/app/models/modules/base-module.ts @@ -25,9 +25,11 @@ * Created by obarda on 2/4/2016. */ 'use strict'; +import * as _ from "lodash"; import {PropertyModel} from "../properties"; import {ArtifactModel} from "../artifacts"; import {CommonUtils} from "../../utils/common-utils"; + export class Module { public name:string; diff --git a/catalog-ui/src/app/models/path-elements.ts b/catalog-ui/src/app/models/path-elements.ts new file mode 100644 index 0000000000..b4dbec4383 --- /dev/null +++ b/catalog-ui/src/app/models/path-elements.ts @@ -0,0 +1,5 @@ +import {ForwardingPathLink} from './forwarding-path-link'; +export class PathElements { + public listToscaDataDefinition:Array<ForwardingPathLink> = []; + public ownerId: string; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/models/policy-metadata.ts b/catalog-ui/src/app/models/policy-metadata.ts new file mode 100644 index 0000000000..33e0185e33 --- /dev/null +++ b/catalog-ui/src/app/models/policy-metadata.ts @@ -0,0 +1,33 @@ +export class PolicyMetadata { + public uniqueId: string; + public type: string; + public version: string; + public description: string; + public creationTime: number; + public modificationTime: number; + public highestVersion: boolean; + public empty: boolean; + + deserialize (response): PolicyMetadata { + this.uniqueId = response.uniqueId; + this.type = response.type; + this.version = response.version; + this.description = response.description; + this.creationTime = response.creationTime; + this.modificationTime = response.modificationTime; + this.highestVersion = response.highestVersion; + this.empty = response.empty; + + return this; + } +} + +export interface PolicyTpes { + policyTypes: Array<PolicyMetadata>; + excludeMapping: ExcludedPolicyTypes; +} + +export interface ExcludedPolicyTypes { + componentType: string; + excludedPolicyTypes: Array<string>; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts b/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts index 8ed5c734c0..33e83ce00a 100644 --- a/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts +++ b/catalog-ui/src/app/models/properties-inputs/derived-fe-property.ts @@ -18,14 +18,18 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { SchemaPropertyGroupModel, SchemaProperty } from '../aschema-property'; -import { DerivedPropertyType, PropertyBEModel } from '../../models'; +import { DerivedPropertyType, PropertyBEModel, PropertyFEModel } from '../../models'; import { PROPERTY_TYPES } from 'app/utils'; import { UUID } from "angular2-uuid"; export class DerivedFEProperty extends PropertyBEModel { - valueObj: any; + valueObj: any; + valueObjIsValid: boolean; + valueObjOrig: any; + valueObjIsChanged: boolean; parentName: string; propertiesName: string; //"network_assignments#ipv4_subnet#use_ipv4 = parentPath + name derivedDataType: DerivedPropertyType; @@ -36,6 +40,7 @@ export class DerivedFEProperty extends PropertyBEModel { isChildOfListOrMap: boolean; canBeDeclared: boolean; mapKey: string; + mapKeyError: string; constructor(property: PropertyBEModel, parentName?: string, createChildOfListOrMap?: boolean, key?:string, value?:any) { if (!createChildOfListOrMap) { //creating a standard derived prop @@ -54,17 +59,44 @@ export class DerivedFEProperty extends PropertyBEModel { if (property.type == PROPERTY_TYPES.LIST) { this.mapKey = property.schema.property.type.split('.').pop(); + this.mapKeyError = null; this.type = property.schema.property.type; } else { //map - this.mapKey = key || ""; + if (key) { + this.mapKey = key; + this.mapKeyError = null; + } else { + this.mapKey = ''; + this.mapKeyError = 'Key cannot be empty.'; + } this.type = property.type; } this.valueObj = (this.type == PROPERTY_TYPES.JSON && typeof value == 'object') ? JSON.stringify(value) : value; this.schema = new SchemaPropertyGroupModel(new SchemaProperty(property.schema.property)); + this.updateValueObjOrig(); } + this.valueObjIsValid = true; this.derivedDataType = this.getDerivedPropertyType(); } - + + public getActualMapKey() { + return (this.mapKeyError) ? this.name : this.mapKey; + } + + public updateValueObj(valueObj:any, isValid:boolean) { + this.valueObj = PropertyFEModel.cleanValueObj(valueObj); + this.valueObjIsValid = isValid; + this.valueObjIsChanged = this.hasValueObjChanged(); + } + + public updateValueObjOrig() { + this.valueObjOrig = _.cloneDeep(this.valueObj); + this.valueObjIsChanged = false; + } + + public hasValueObjChanged() { + return !_.isEqual(this.valueObj, this.valueObjOrig); + } } export class DerivedFEPropertyMap { [parentPath: string]: Array<DerivedFEProperty>; diff --git a/catalog-ui/src/app/models/properties-inputs/input-be-model.ts b/catalog-ui/src/app/models/properties-inputs/input-be-model.ts index f6548a352c..942d775c6e 100644 --- a/catalog-ui/src/app/models/properties-inputs/input-be-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/input-be-model.ts @@ -41,9 +41,6 @@ export class InputBEModel extends PropertyBEModel { this.inputPath = input.inputPath; } - public toJSON = (): any => { - }; - } export interface ComponentInstanceModel extends InputBEModel { diff --git a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts index 630374aac3..c349f41ea2 100644 --- a/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/input-fe-model.ts @@ -18,15 +18,22 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { SchemaPropertyGroupModel, SchemaProperty } from "../aschema-property"; -import { PropertyBEModel } from "../../models"; +import {PropertyFEModel} from "../../models"; import {PROPERTY_DATA} from "../../utils/constants"; import {InputBEModel} from "./input-be-model"; +import {DerivedPropertyType} from "./property-be-model"; export class InputFEModel extends InputBEModel { isSimpleType: boolean; relatedPropertyValue: any; relatedPropertyName: string; + defaultValueObj:any; + defaultValueObjIsValid:boolean; + defaultValueObjOrig:any; + defaultValueObjIsChanged:boolean; + derivedDataType: DerivedPropertyType; constructor(input?: InputBEModel) { super(input); @@ -37,7 +44,37 @@ export class InputFEModel extends InputBEModel { this.relatedPropertyValue = relatedProperty.value; this.relatedPropertyName = relatedProperty.name; } + this.derivedDataType = this.getDerivedPropertyType(); + this.resetDefaultValueObjValidation(); + this.updateDefaultValueObjOrig(); } } + public updateDefaultValueObj(defaultValueObj:any, isValid:boolean) { + this.defaultValueObj = PropertyFEModel.cleanValueObj(defaultValueObj); + this.defaultValueObjIsValid = isValid; + this.defaultValueObjIsChanged = this.hasDefaultValueChanged(); + } + + public updateDefaultValueObjOrig() { + this.defaultValueObjOrig = _.cloneDeep(this.defaultValueObj); + this.defaultValueObjIsChanged = false; + } + + public getJSONDefaultValue(): string { + return PropertyFEModel.stringifyValueObj(this.defaultValueObj, this.schema.property.type, this.derivedDataType); + } + + public getDefaultValueObj(): any { + return PropertyFEModel.parseValueObj(this.defaultValue, this.type, this.derivedDataType); + } + + public resetDefaultValueObjValidation() { + this.defaultValueObjIsValid = true; + } + + hasDefaultValueChanged(): boolean { + return !_.isEqual(this.defaultValueObj, this.defaultValueObjOrig); + } + }
\ No newline at end of file diff --git a/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts b/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts index 6faa6ada84..a0c087bdc2 100644 --- a/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts +++ b/catalog-ui/src/app/models/properties-inputs/property-fe-model.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {SchemaPropertyGroupModel, SchemaProperty} from '../aschema-property'; import { PROPERTY_DATA, PROPERTY_TYPES } from 'app/utils'; import { FilterPropertiesAssignmentData, PropertyBEModel, DerivedPropertyType, DerivedFEPropertyMap, DerivedFEProperty } from 'app/models'; @@ -34,6 +35,10 @@ export class PropertyFEModel extends PropertyBEModel { propertiesName: string; uniqueId: string; valueObj: any; //this is the only value we relate to in the html templates + valueObjValidation: any; + valueObjIsValid: boolean; + valueObjOrig: any; //this is valueObj representation as saved in server + valueObjIsChanged: boolean; derivedDataType: DerivedPropertyType; constructor(property: PropertyBEModel){ @@ -44,19 +49,50 @@ export class PropertyFEModel extends PropertyBEModel { this.derivedDataType = this.getDerivedPropertyType(); this.flattenedChildren = []; this.propertiesName = this.name; + this.valueObj = null; + this.updateValueObjOrig(); + this.resetValueObjValidation(); } - public getJSONValue = (): string => { - //If type is JSON, need to try parsing it before we stringify it so that it appears property in TOSCA - change per Bracha due to AMDOCS - //TODO: handle this.derivedDataType == DerivedPropertyType.MAP - if (this.derivedDataType == DerivedPropertyType.LIST && this.schema.property.type == PROPERTY_TYPES.JSON) { - try { - return JSON.stringify(this.valueObj.map(item => (typeof item == 'string')? JSON.parse(item) : item)); - } catch (e){} + public updateValueObj(valueObj:any, isValid:boolean) { + this.valueObj = PropertyFEModel.cleanValueObj(valueObj); + this.valueObjValidation = this.valueObjIsValid = isValid; + this.valueObjIsChanged = this.hasValueObjChanged(); + } + + public updateValueObjOrig() { + this.valueObjOrig = _.cloneDeep(this.valueObj); + this.valueObjIsChanged = false; + } + + public calculateValueObjIsValid(valueObjValidation?: any) { + valueObjValidation = (valueObjValidation !== undefined) ? valueObjValidation : this.valueObjValidation; + if (valueObjValidation instanceof Array) { + return valueObjValidation.every((v) => this.calculateValueObjIsValid(v)); + } else if (valueObjValidation instanceof Object) { + return Object.keys(valueObjValidation).every((k) => this.calculateValueObjIsValid(valueObjValidation[k])); } + return Boolean(valueObjValidation); + } - return (this.derivedDataType == DerivedPropertyType.SIMPLE) ? this.valueObj : JSON.stringify(this.valueObj); + public resetValueObjValidation() { + if (this.derivedDataType === DerivedPropertyType.SIMPLE) { + this.valueObjValidation = null; + } else if (this.derivedDataType === DerivedPropertyType.LIST) { + this.valueObjValidation = []; + } else { + this.valueObjValidation = {}; + } + this.valueObjIsValid = true; + } + + public getJSONValue = (): string => { + return PropertyFEModel.stringifyValueObj(this.valueObj, this.schema.property.type, this.derivedDataType); + } + + public getValueObj = (): any => { + return PropertyFEModel.parseValueObj(this.value, this.type, this.derivedDataType, this.defaultValue); } public setNonDeclared = (childPath?: string): void => { @@ -106,12 +142,94 @@ export class PropertyFEModel extends PropertyBEModel { public childPropUpdated = (childProp: DerivedFEProperty): void => { let parentNames = this.getParentNamesArray(childProp.propertiesName, []); if (parentNames.length) { - _.set(this.valueObj, parentNames.join('.'), childProp.valueObj); + const childPropName = parentNames.join('.'); + // unset value only if is null and valid, and not in a list + if (childProp.valueObj === null && childProp.valueObjIsValid) { + const parentChildProp = this.flattenedChildren.find((ch) => ch.propertiesName === childProp.parentName) || this; + if (parentChildProp.derivedDataType !== DerivedPropertyType.LIST) { + _.unset(this.valueObj, childPropName); + this.valueObj = PropertyFEModel.cleanValueObj(this.valueObj); + } else { + _.set(this.valueObj, childPropName, null); + } + } else { + _.set(this.valueObj, childPropName, childProp.valueObj); + } + if (childProp.valueObjIsChanged) { + _.set(this.valueObjValidation, childPropName, childProp.valueObjIsValid); + this.valueObjIsValid = childProp.valueObjIsValid && this.calculateValueObjIsValid(); + this.valueObjIsChanged = true; + } else { + _.unset(this.valueObjValidation, childPropName); + this.valueObjIsValid = this.calculateValueObjIsValid(); + this.valueObjIsChanged = this.hasValueObjChanged(); + } + } + }; + + childPropMapKeyUpdated = (childProp: DerivedFEProperty, newMapKey: string, forceValidate: boolean = false) => { + if (!childProp.isChildOfListOrMap || childProp.derivedDataType !== DerivedPropertyType.MAP) { + return; + } + + const childParentNames = this.getParentNamesArray(childProp.parentName); + const oldActualMapKey = childProp.getActualMapKey(); + + childProp.mapKey = newMapKey; + if (childProp.mapKey === null) { // null -> remove map key + childProp.mapKeyError = null; + } else if (!childProp.mapKey) { + childProp.mapKeyError = 'Key cannot be empty.'; + } else if (this.flattenedChildren + .filter((fch) => fch !== childProp && fch.parentName === childProp.parentName) // filter sibling child props + .map((fch) => fch.mapKey) + .indexOf(childProp.mapKey) !== -1) { + childProp.mapKeyError = 'This key already exists.'; + } else { + childProp.mapKeyError = null; + } + const newActualMapKey = childProp.getActualMapKey(); + const newMapKeyIsValid = !childProp.mapKeyError; + + // if mapKey was changed, then replace the old key with the new one + if (newActualMapKey !== oldActualMapKey) { + const oldChildPropNames = childParentNames.concat([oldActualMapKey]); + const newChildPropNames = (newActualMapKey) ? childParentNames.concat([newActualMapKey]) : null; + + // add map key to valueObj and valueObjValidation + if (newChildPropNames) { + const newChildVal = _.get(this.valueObj, oldChildPropNames); + if (newChildVal !== undefined) { + _.set(this.valueObj, newChildPropNames, newChildVal); + _.set(this.valueObjValidation, newChildPropNames, _.get(this.valueObjValidation, oldChildPropNames, childProp.valueObjIsValid)); + } + } + + // remove map key from valueObj and valueObjValidation + _.unset(this.valueObj, oldChildPropNames); + _.unset(this.valueObjValidation, oldChildPropNames); + + // force validate after map key change + forceValidate = true; + } + + if (forceValidate) { + // add custom entry for map key validation: + const childMapKeyNames = childParentNames.concat(`%%KEY:${childProp.name}%%`); + if (newActualMapKey) { + _.set(this.valueObjValidation, childMapKeyNames, newMapKeyIsValid); + } else { + _.unset(this.valueObjValidation, childMapKeyNames); + } + + this.valueObjIsValid = newMapKeyIsValid && this.calculateValueObjIsValid(); + this.valueObjIsChanged = this.hasValueObjChanged(); } }; /* Returns array of individual parents for given prop path, with list/map UUIDs replaced with index/mapkey */ public getParentNamesArray = (parentPropName: string, parentNames?: Array<string>): Array<string> => { + parentNames = parentNames || []; if (parentPropName.indexOf("#") == -1) { return parentNames; } //finished recursing parents. return let parentProp: DerivedFEProperty = this.flattenedChildren.find(prop => prop.propertiesName === parentPropName); @@ -119,7 +237,7 @@ export class PropertyFEModel extends PropertyBEModel { if (parentProp.isChildOfListOrMap) { if (parentProp.derivedDataType == DerivedPropertyType.MAP) { - nameToInsert = parentProp.mapKey; + nameToInsert = parentProp.getActualMapKey(); } else { //LIST let siblingProps = this.flattenedChildren.filter(prop => prop.parentName == parentProp.parentName).map(prop => prop.propertiesName); nameToInsert = siblingProps.indexOf(parentProp.propertiesName).toString(); @@ -130,5 +248,72 @@ export class PropertyFEModel extends PropertyBEModel { return this.getParentNamesArray(parentProp.parentName, parentNames); //continue recursing } + public hasValueObjChanged() { + return !_.isEqual(this.valueObj, this.valueObjOrig); + } + static stringifyValueObj(valueObj: any, propertyType: PROPERTY_TYPES, propertyDerivedType: DerivedPropertyType): string { + // if valueObj is null, return null + if (valueObj === null || valueObj === undefined) { + return null; + } + + //If type is JSON, need to try parsing it before we stringify it so that it appears property in TOSCA - change per Bracha due to AMDOCS + //TODO: handle this.derivedDataType == DerivedPropertyType.MAP + if (propertyDerivedType == DerivedPropertyType.LIST && propertyType == PROPERTY_TYPES.JSON) { + try { + return JSON.stringify(valueObj.map(item => (typeof item == 'string') ? JSON.parse(item) : item)); + } catch (e){} + } + + // if type is anything but string, then stringify valueObj + if ((typeof valueObj) !== 'string') { + return JSON.stringify(valueObj); + } + + // return string value as is + return valueObj; + } + + static parseValueObj(value: string, propertyType: PROPERTY_TYPES, propertyDerivedType: DerivedPropertyType, defaultValue?: string): any { + let valueObj; + if (propertyDerivedType === DerivedPropertyType.SIMPLE) { + valueObj = value || defaultValue || null; // use null for empty value object + if (valueObj && + propertyType !== PROPERTY_TYPES.STRING && + propertyType !== PROPERTY_TYPES.JSON && + PROPERTY_DATA.SCALAR_TYPES.indexOf(<string>propertyType) == -1) { + valueObj = JSON.parse(value); // the value object contains the real value ans not the value as string + } + } else if (propertyDerivedType == DerivedPropertyType.LIST) { + valueObj = _.merge([], JSON.parse(defaultValue || '[]'), JSON.parse(value || '[]')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined. + } else { + valueObj = _.merge({}, JSON.parse(defaultValue || '{}'), JSON.parse(value || '{}')); // value object should be merged value and default value. Value takes higher precedence. Set value object to empty obj if undefined. + } + return valueObj; + }; + + static cleanValueObj(valueObj: any, unsetEmpty?: boolean): any { + // By default - unsetEmpty undefined - will make valueObj cleaned (no null or empty objects, but array will keep null or empty objects). + if (valueObj === undefined || valueObj === null || valueObj === '') { + return null; + } + if (valueObj instanceof Array) { + const cleanArr = valueObj.map((v) => PropertyFEModel.cleanValueObj(v)).filter((v) => v !== null); + valueObj.splice(0, valueObj.length, ...cleanArr) + } else if (valueObj instanceof Object) { + Object.keys(valueObj).forEach((k) => { + // clean each item in the valueObj (by default, unset empty objects) + valueObj[k] = PropertyFEModel.cleanValueObj(valueObj[k], unsetEmpty !== undefined ? unsetEmpty : true); + if (valueObj[k] === null) { + delete valueObj[k]; + } + }); + // if unsetEmpty flag is true and valueObj is empty + if (unsetEmpty && !Object.keys(valueObj).length) { + return null; + } + } + return valueObj; + } } diff --git a/catalog-ui/src/app/models/properties.ts b/catalog-ui/src/app/models/properties.ts index 7a1f1a39ef..7ff27706b0 100644 --- a/catalog-ui/src/app/models/properties.ts +++ b/catalog-ui/src/app/models/properties.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {SchemaPropertyGroupModel, SchemaProperty} from "./aschema-property"; import {InputPropertyBase} from "./input-property-base"; import {PropertyBEModel} from "./properties-inputs/property-be-model"; diff --git a/catalog-ui/src/app/models/requirement.ts b/catalog-ui/src/app/models/requirement.ts index d880456b06..65428b398e 100644 --- a/catalog-ui/src/app/models/requirement.ts +++ b/catalog-ui/src/app/models/requirement.ts @@ -22,7 +22,9 @@ * Created by obarda on 4/20/2016. */ 'use strict'; +import * as _ from "lodash"; import {RequirementCapabilityModel} from "./capability"; + //this is an object contains keys, when each key has matching array. // for example: key = tosca.capabilities.network. and the match array is array of requirements objects export class RequirementsGroup { @@ -44,6 +46,7 @@ export class Requirement implements RequirementCapabilityModel{ name:string; ownerId:string; ownerName:string; + parentName: string; node:string; uniqueId:string; relationship:string; @@ -60,6 +63,7 @@ export class Requirement implements RequirementCapabilityModel{ this.name = requirement.name; this.ownerId = requirement.ownerId; this.ownerName = requirement.ownerName; + this.parentName = requirement.parentName; this.node = requirement.node; this.uniqueId = requirement.uniqueId; this.relationship = requirement.relationship; diff --git a/catalog-ui/src/app/modules/directive-module.ts b/catalog-ui/src/app/modules/directive-module.ts index 4039a7683a..1babeefbdb 100644 --- a/catalog-ui/src/app/modules/directive-module.ts +++ b/catalog-ui/src/app/modules/directive-module.ts @@ -72,11 +72,12 @@ import {CompositionGraphGeneralUtils} from "../directives/graphs-v2/composition- import {CompositionGraphLinkUtils} from "../directives/graphs-v2/composition-graph/utils/composition-graph-links-utils"; import {DeploymentGraphGeneralUtils} from "../directives/graphs-v2/deployment-graph/deployment-utils/deployment-graph-general-utils"; import {CompositionGraphPaletteUtils} from "../directives/graphs-v2/composition-graph/utils/composition-graph-palette-utils"; +import {CompositionGraphZoneUtils} from "../directives/graphs-v2/composition-graph/utils/composition-graph-zone-utils"; import {MatchCapabilitiesRequirementsUtils} from "../directives/graphs-v2/composition-graph/utils/match-capability-requierment-utils"; -import {AssetPopoverDirective} from "../directives/graphs-v2/asset-popover/asset-popover"; -import {downgradeComponent} from "@angular/upgrade/static"; import {CapabilitiesListDirective} from "../directives/capabilities-and-requirements/capability/capabilities-list-directive"; import {RequirementsListDirective} from "../directives/capabilities-and-requirements/requirement/requirements-list-directive"; +import {ServicePathGraphUtils} from "../directives/graphs-v2/composition-graph/utils/composition-graph-service-path-utils"; +import {PaletteAnimationComponent} from './../ng2/components/ui/palette-animation/palette-animation.component'; let moduleName:string = 'Sdc.Directives'; let directiveModule:ng.IModule = angular.module(moduleName, []); @@ -157,7 +158,10 @@ directiveModule.service('CompositionGraphNodesUtils', CompositionGraphNodesUtils directiveModule.service('CompositionGraphGeneralUtils', CompositionGraphGeneralUtils); directiveModule.service('CompositionGraphLinkUtils', CompositionGraphLinkUtils); directiveModule.service('CompositionGraphPaletteUtils', CompositionGraphPaletteUtils); +directiveModule.service('CompositionGraphZoneUtils', CompositionGraphZoneUtils); directiveModule.service('MatchCapabilitiesRequirementsUtils', MatchCapabilitiesRequirementsUtils); +directiveModule.service('ServicePathGraphUtils', ServicePathGraphUtils); + // // //Deployment Graph Utils directiveModule.service('DeploymentGraphGeneralUtils', DeploymentGraphGeneralUtils); @@ -169,15 +173,70 @@ directiveModule.directive('requirementsList', RequirementsListDirective.factory) // *** NG2 Components (downgraded) *** // -import {MenuListNg2Component} from "../ng2/components/downgrade-wrappers/menu-list-ng2/menu-list-ng2.component"; -import {TopNavComponent} from "../ng2/components/layout/top-nav/top-nav.component"; +import { downgradeComponent } from "@angular/upgrade/static"; +import { MenuListNg2Component } from "../ng2/components/downgrade-wrappers/menu-list-ng2/menu-list-ng2.component"; +import { TopNavComponent } from "../ng2/components/layout/top-nav/top-nav.component"; +import { ZoneContainerComponent } from "../ng2/components/ui/canvas-zone/zone-container.component"; +import { ZoneInstanceComponent } from "../ng2/components/ui/canvas-zone/zone-instance/zone-instance.component"; +import { PropertiesAssignmentComponent } from "../ng2/pages/properties-assignment/properties-assignment.page.component"; +import { SearchWithAutoCompleteComponent } from "../ng2/components/ui/search-with-autocomplete/search-with-autocomplete.component"; +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'; directiveModule.directive('menuListNg2', downgradeComponent({ component: MenuListNg2Component, inputs: ['props'] }) as angular.IDirectiveFactory); + directiveModule.directive('topNav', downgradeComponent({ component: TopNavComponent, inputs: ['version', 'menuModel', 'topLvlSelectedIndex', 'hideSearch', 'searchTerm', 'notificationIconCallback'], outputs: ['searchTermChange'] }) as ng.IDirectiveFactory); + +directiveModule.directive('ng2ZoneContainer', downgradeComponent({ + component: ZoneContainerComponent, + inputs: ['title', 'count', 'class', 'showZone', 'minifyZone'], + outputs: [] +}) as angular.IDirectiveFactory); + +directiveModule.directive('ng2ZoneInstance', downgradeComponent({ + component: ZoneInstanceComponent, + inputs: ['config', 'isActive', 'activeInstanceMode', 'defaultIconText'], + outputs: ['modeChange'] +}) as angular.IDirectiveFactory); + +directiveModule.directive('propertiesAssignment', downgradeComponent({ + component: PropertiesAssignmentComponent +}) as angular.IDirectiveFactory); + +directiveModule.directive('ng2SearchWithAutocomplete', downgradeComponent({ + component: SearchWithAutoCompleteComponent, + inputs: ['searchPlaceholder', 'searchBarClass', 'autoCompleteValues'], + outputs: ['searchChanged', 'searchButtonClicked'] +}) as angular.IDirectiveFactory); + +directiveModule.directive('ng2PaletteAnimation', downgradeComponent({ + component: PaletteAnimationComponent, + inputs: ['from', 'to', 'icon' ], + outputs: [] + }) as angular.IDirectiveFactory); + +directiveModule.directive('ng2PalettePopupPanel', downgradeComponent({ + component: PalettePopupPanelComponent, + inputs: [], + outputs: [] +}) as angular.IDirectiveFactory); + +directiveModule.directive('ng2ServicePath', downgradeComponent({ + component: ServicePathComponent, + inputs: ['onCreate', 'service'], + outputs: [] +}) as angular.IDirectiveFactory); + +directiveModule.directive('ng2ServicePathSelector', downgradeComponent({ + component: ServicePathSelectorComponent, + inputs: ['drawPath', 'deletePaths', 'service', 'selectedPathId'], + outputs: [] +}) as angular.IDirectiveFactory); diff --git a/catalog-ui/src/app/modules/service-module.ts b/catalog-ui/src/app/modules/service-module.ts index 387332cae5..c934b630c8 100644 --- a/catalog-ui/src/app/modules/service-module.ts +++ b/catalog-ui/src/app/modules/service-module.ts @@ -7,9 +7,9 @@ * 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. @@ -48,11 +48,14 @@ import {downgradeInjectable} from "@angular/upgrade/static"; import {ModalService} from "../ng2/services/modal.service"; import {ComponentServiceNg2} from "../ng2/services/component-services/component.service"; import {ServiceServiceNg2} from "../ng2/services/component-services/service.service"; +import {ComponentServiceFactoryNg2} from "../ng2/services/component-services/component.service.factory"; import {ConnectionWizardService} from "../ng2/pages/connection-wizard/connection-wizard.service"; import {ComponentInstanceServiceNg2} from "../ng2/services/component-instance-services/component-instance.service"; import {UserService as UserServiceNg2} from "../ng2/services/user.service"; import {PluginsService} from "../ng2/services/plugins.service"; import {EventBusService} from "../ng2/services/event-bus.service"; +import {PoliciesService as PoliciesServiceNg2} from "../ng2/services/policies.service"; +import {DynamicComponentService} from "app/ng2/services/dynamic-component.service"; let moduleName:string = 'Sdc.Services'; let serviceModule:ng.IModule = angular.module(moduleName, []); @@ -92,6 +95,7 @@ serviceModule.factory('Sdc.Services.CategoryResourceService', CategoryResourceSe // Angular2 upgraded services - This is in order to use the service in angular1 till we finish remove all angular1 code serviceModule.factory('ComponentServiceNg2', downgradeInjectable(ComponentServiceNg2)); +serviceModule.factory('ComponentServiceFactoryNg2', downgradeInjectable(ComponentServiceFactoryNg2)); serviceModule.factory('ServiceServiceNg2', downgradeInjectable(ServiceServiceNg2)); serviceModule.factory('ModalServiceNg2', downgradeInjectable(ModalService)); serviceModule.factory('ConnectionWizardServiceNg2', downgradeInjectable(ConnectionWizardService)); @@ -99,3 +103,5 @@ serviceModule.factory('ComponentInstanceServiceNg2', downgradeInjectable(Compone serviceModule.factory('UserServiceNg2', downgradeInjectable(UserServiceNg2)); serviceModule.factory('PluginsService', downgradeInjectable(PluginsService)); serviceModule.factory('EventBusService', downgradeInjectable(EventBusService)); +serviceModule.factory('PoliciesServiceNg2', downgradeInjectable(PoliciesServiceNg2)); +serviceModule.factory('DynamicComponentService', downgradeInjectable(DynamicComponentService)); diff --git a/catalog-ui/src/app/ng2/app.module.ts b/catalog-ui/src/app/ng2/app.module.ts index ffeb1fd89f..727f0fe8f5 100644 --- a/catalog-ui/src/app/ng2/app.module.ts +++ b/catalog-ui/src/app/ng2/app.module.ts @@ -28,7 +28,8 @@ import {UpgradeModule} from '@angular/upgrade/static'; import {PropertiesAssignmentModule} from './pages/properties-assignment/properties-assignment.module'; import { DataTypesServiceProvider, SharingServiceProvider, CookieServiceProvider, StateServiceFactory, - StateParamsServiceFactory, CacheServiceProvider, EventListenerServiceProvider, ScopeServiceFactory + StateParamsServiceFactory, CacheServiceProvider, EventListenerServiceProvider, ScopeServiceFactory, + NotificationServiceProvider } from "./utils/ng1-upgraded-provider"; import {ConfigService} from "./services/config.service"; import {HttpModule} from '@angular/http'; @@ -36,6 +37,7 @@ import {HttpService} from './services/http.service'; import {AuthenticationService} from './services/authentication.service'; import {Cookie2Service} from "./services/cookie.service"; import {ComponentServiceNg2} from "./services/component-services/component.service"; +import {ComponentServiceFactoryNg2} from "./services/component-services/component.service.factory"; import {ServiceServiceNg2} from "./services/component-services/service.service"; import {ComponentInstanceServiceNg2} from "./services/component-instance-services/component-instance.service"; import {ModalService} from "./services/modal.service"; @@ -43,12 +45,18 @@ import {UiElementsModule} from "./components/ui/ui-elements.module"; import {ConnectionWizardModule} from "./pages/connection-wizard/connection-wizard.module"; import {LayoutModule} from "./components/layout/layout.module"; import {UserService} from "./services/user.service"; +import {PoliciesService} from "./services/policies.service"; +import {DynamicComponentService} from "./services/dynamic-component.service"; import {SdcConfig} from "./config/sdc-config.config"; import { TranslateModule } from "./shared/translator/translate.module"; import { TranslationServiceConfig } from "./config/translation.service.config"; +import {ServicePathCreatorModule} from './pages/service-path-creator/service-path-creator.module'; +import {ServicePathsListModule} from './pages/service-paths-list/service-paths-list.module'; import {PluginFrameModule} from "./components/ui/plugin/plugin-frame.module"; import {PluginsService} from "./services/plugins.service"; import {EventBusService} from "./services/event-bus.service"; +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'; export const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule)); @@ -76,7 +84,11 @@ export function configServiceFactory(config:ConfigService) { //We need to import them here since we use them in angular1 ConnectionWizardModule, PropertiesAssignmentModule, - PluginFrameModule + PluginFrameModule, + ServicePathCreatorModule, + ServicePathsListModule, + ServicePathModule, + ServicePathSelectorModule ], exports: [], entryComponents: [], @@ -85,18 +97,22 @@ export function configServiceFactory(config:ConfigService) { SharingServiceProvider, CookieServiceProvider, StateServiceFactory, - ScopeServiceFactory, StateParamsServiceFactory, + ScopeServiceFactory, CacheServiceProvider, EventListenerServiceProvider, + NotificationServiceProvider, AuthenticationService, Cookie2Service, ConfigService, ComponentServiceNg2, + ComponentServiceFactoryNg2, ModalService, ServiceServiceNg2, HttpService, UserService, + PoliciesService, + DynamicComponentService, SdcConfig, ComponentInstanceServiceNg2, TranslationServiceConfig, diff --git a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts index b8b0e80861..12f8df8296 100644 --- a/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts +++ b/catalog-ui/src/app/ng2/components/layout/top-nav/top-nav.component.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {Component, Inject, Input, Output, EventEmitter} from "@angular/core"; import {IHostedApplication, IUserProperties} from "app/models"; import {MenuItemGroup, MenuItem} from "app/utils"; @@ -142,9 +143,9 @@ export class TopNavComponent { } } - goToState(state:string, params:any):Promise<boolean> { + goToState(state:string, params:Array<any>):Promise<boolean> { return new Promise((resolve, reject) => { - this.$state.go(state, params || undefined); + this.$state.go(state, params && params.length > 0 ? [0] : undefined); resolve(true); }); } diff --git a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html index ddda82ab58..cc8aed70d4 100644 --- a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html +++ b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.html @@ -7,7 +7,7 @@ <checkbox [label]="'All'" [(checked)]="allSelected" (checkedChange)="selectAll()" data-tests-id="filter-checkbox-all"></checkbox> </div> <div *ngFor="let type of typesOptions"> - <checkbox [label]="type" [(checked)]="selectedTypes[type]" (checkedChange)="onTypeSelected(type)"></checkbox> + <checkbox [label]="type" [(checked)]="selectedTypes[type]" (checkedChange)="onTypeSelected(type)" [attr.data-tests-id]="'filter-checkbox-' + type.toLowerCase()"></checkbox> </div> </div> <div class="field"> diff --git a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts index 5a9bfbcecb..fe2c7bb09a 100644 --- a/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/filter-properties-assignment/filter-properties-assignment.component.ts @@ -21,6 +21,7 @@ /** * Created by rc2122 on 5/16/2017. */ +import * as _ from "lodash"; import {Component, Input, Output, EventEmitter, ViewChild} from '@angular/core'; import {ButtonModel, ButtonsModelMap, FilterPropertiesAssignmentData} from "app/models"; import * as sdcConfig from "../../../../../../configurations/dev" diff --git a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts index dc0a02c277..1698157e90 100644 --- a/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/hierarchy-navigtion/hierarchy-navigation.component.ts @@ -37,12 +37,10 @@ export class HierarchyNavigationComponent { onClick = ($event, item) => { $event.stopPropagation(); - this.selectedItem = item; this.updateSelected.emit(item); }; onSelectedUpdate = ($event) => { - this.selectedItem = $event; this.updateSelected.emit($event); } } diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html index 57e0474c66..b7cde7eb23 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.html @@ -30,11 +30,11 @@ <dynamic-element class="value-input" *ngIf="input.isSimpleType" pattern="validationUtils.getValidationPattern(input.type)" - [(value)]="input.defaultValue" + [value]="input.defaultValueObj" [type]="input.type" [name]="input.name" - (change)="onInputValueChanged(input);" - [readonly]="readonly"> + (elementChanged)="onInputChanged(input, $event)" + [readonly]="readonly"> </dynamic-element> <div class="delete-button-container"> <span *ngIf="input.instanceUniqueId && !readonly" class="sprite-new delete-btn" (click)="openDeleteModal(input)" data-tests-id="delete-input-button"></span> diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less index 0545874f53..d709f3f0c5 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.less @@ -11,7 +11,7 @@ text-align:left; .inner-cell-div{ - width: 100%; + max-width: 100%; text-overflow: ellipsis; overflow: hidden; height: 20px; @@ -85,8 +85,8 @@ border-right:#d2d2d2 solid 1px; } &.col1 { - flex: 1 0 200px; - max-width:300px; + flex: 1 0 210px; + max-width:210px; display: flex; justify-content: space-between; @@ -121,7 +121,6 @@ .value-input { flex: 1; - max-height: 24px; border: none; background-color: inherit; diff --git a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts index 0add1cd707..ebecbc9390 100644 --- a/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/inputs-table/inputs-table.component.ts @@ -36,7 +36,7 @@ export class InputsTableComponent { @Input() instanceNamesMap: Map<string, string>; @Input() readonly:boolean; @Input() isLoading:boolean; - @Output() inputValueChanged: EventEmitter<any> = new EventEmitter<any>(); + @Output() inputChanged: EventEmitter<any> = new EventEmitter<any>(); @Output() deleteInput: EventEmitter<any> = new EventEmitter<any>(); selectedInputToDelete:InputFEModel; @@ -44,8 +44,9 @@ export class InputsTableComponent { constructor(private modalService: ModalService){ } - onInputValueChanged = (input) => { - this.inputValueChanged.emit(input); + onInputChanged = (input, event) => { + input.updateDefaultValueObj(event.value, event.isValid); + this.inputChanged.emit(input); }; onDeleteInput = () => { diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html index 14b6c7d4c0..4805875d83 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.html @@ -10,7 +10,16 @@ </div> <div class="table-cell" *ngIf="!canBeDeclared && !property.isChildOfListOrMap">{{property.name}}</div> <!-- simple children of complex type within map or list --> <div class="table-cell map-entry" *ngIf="property.isChildOfListOrMap && propType == derivedPropertyTypes.MAP"><!-- map left cell --> - <input [value]="property.mapKey" #mapKey (change)="mapKeyChanged.emit(mapKey)" [readonly]="readonly" type="text" [ngClass]="{'disabled':readonly, 'error':!mapKey.validity.valid}" required/> + <!--<input [value]="property.mapKey" [placeholder]="property.name" (input)="mapKeyChanged.emit($event.target.value)" [readonly]="readonly" type="text" [ngClass]="{'disabled':readonly, 'error':property.mapKeyError}" required/>--> + <dynamic-element #mapKeyInput + class="value-input" + pattern="validationUtils.getValidationPattern(string)" + [value]="property.mapKey" + type="string" + [name]="property.name" + (elementChanged)="mapKeyChanged.emit($event.value)" + [readonly]="readonly" + ></dynamic-element> </div> </ng-container> <!-- RIGHT CELL OR FULL WIDTH CELL--> @@ -18,11 +27,11 @@ <div class="table-cell"> <dynamic-element class="value-input" pattern="validationUtils.getValidationPattern(property.type)" - [(value)]="property.valueObj" + [value]="property.valueObj" [type]="property.isDeclared ? 'string' : property.type" [name]="property.name" [path]="property.propertiesName" - (valueChange)="valueChanged.emit();" + (elementChanged)="onElementChanged($event)" [readonly]="readonly || property.isDeclared || property.isDisabled" ></dynamic-element> </div> @@ -54,8 +63,8 @@ [propertyNameSearchText]="propertyNameSearchText" [readonly]="readonly" [hasChildren]="getHasChildren(prop)" - (valueChanged)="childValueChanged(prop)" - (mapKeyChanged)="removeValueFromParent(prop, $event)" + (propertyChanged)="childValueChanged(prop)" + (mapKeyChanged)="updateChildKeyInParent(prop, $event)" (expandChild)="expandChildById($event)" (deleteItem)="deleteListOrMapItem($event)" (clickOnPropertyRow)="onClickPropertyRow($event)" diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts index 04cb26d030..6f7e57b643 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/dynamic-property/dynamic-property.component.ts @@ -18,13 +18,15 @@ * ============LICENSE_END========================================================= */ -import {Component, Input, Output, EventEmitter} from "@angular/core"; +import * as _ from "lodash"; +import {Component, Input, Output, EventEmitter, ViewChild, ComponentRef} from "@angular/core"; import { PropertyFEModel, DerivedFEProperty, DerivedPropertyType } from "app/models"; import { PROPERTY_TYPES } from 'app/utils'; import { DataTypeService } from "../../../../services/data-type.service"; import { trigger, state, style, transition, animate } from '@angular/core'; import {PropertiesUtils} from "../../../../pages/properties-assignment/services/properties.utils"; - +import {IUiElementChangeEvent} from "../../../ui/form-components/ui-element-base.component"; +import {DynamicElementComponent} from "../../../ui/dynamic-element/dynamic-element.component"; @Component({ selector: 'dynamic-property', @@ -49,7 +51,7 @@ export class DynamicPropertyComponent { @Input() hasChildren: boolean; @Input() hasDeclareOption:boolean; - @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>(); + @Output('propertyChanged') emitter: EventEmitter<void> = new EventEmitter<void>(); @Output() expandChild: EventEmitter<string> = new EventEmitter<string>(); @Output() checkProperty: EventEmitter<string> = new EventEmitter<string>(); @Output() deleteItem: EventEmitter<string> = new EventEmitter<string>(); @@ -57,6 +59,7 @@ export class DynamicPropertyComponent { @Output() mapKeyChanged: EventEmitter<string> = new EventEmitter<string>(); @Output() addChildPropsToParent: EventEmitter<Array<DerivedFEProperty>> = new EventEmitter<Array<DerivedFEProperty>>(); + @ViewChild('mapKeyInput') public mapKeyInput: DynamicElementComponent; constructor(private propertiesUtils: PropertiesUtils, private dataTypeService: DataTypeService) { } @@ -68,6 +71,17 @@ export class DynamicPropertyComponent { this.nestedLevel = (this.property.propertiesName.match(/#/g) || []).length; } + ngDoCheck() { + // set custom error for mapKeyInput + if (this.mapKeyInput) { + const mapKeyInputControl = this.mapKeyInput.cmpRef.instance.control; + const mapKeyError = (<DerivedFEProperty>this.property).mapKeyError; + if (mapKeyInputControl.getError('mapKeyError') !== mapKeyError) { + mapKeyInputControl.setErrors({mapKeyError}); + } + } + } + onClickPropertyRow = (property, event) => { // Because DynamicPropertyComponent is recrusive second time the event is fire event.stopPropagation = undefined @@ -91,9 +105,15 @@ export class DynamicPropertyComponent { }).length > 1; } + onElementChanged = (event: IUiElementChangeEvent) => { + this.property.updateValueObj(event.value, event.isValid); + this.emitter.emit(); + }; + createNewChildProperty = (): void => { - let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", undefined); + let newProps: Array<DerivedFEProperty> = this.propertiesUtils.createListOrMapChildren(this.property, "", null); + this.propertiesUtils.assignFlattenedChildrenValues(this.property.valueObj, [newProps[0]], this.property.propertiesName); if (this.property instanceof PropertyFEModel) { this.addChildProps(newProps, this.property.name); } else { @@ -108,28 +128,25 @@ export class DynamicPropertyComponent { this.property.flattenedChildren.splice(insertIndex, 0, ...newProps); //using ES6 spread operator this.expandChildById(newProps[0].propertiesName); - - if(!newProps[0].schema.property.isSimpleType){ - if ( newProps[0].mapKey ) {//prevent update the new item value on parent property valueObj and saving on BE if it is map item, it will be updated and saved only after user enter key (when it is list item- the map key is the es type) - this.updateMapKeyValueOnMainParent(newProps); - if (this.property.getParentNamesArray(newProps[0].propertiesName, []).indexOf('') === -1) { - this.valueChanged.emit(this.property.name); - } - } - } + this.updateMapKeyValueOnMainParent(newProps); + this.emitter.emit(); } } updateMapKeyValueOnMainParent(childrenProps: Array<DerivedFEProperty>){ if (this.property instanceof PropertyFEModel) { + const property: PropertyFEModel = <PropertyFEModel>this.property; //Update only if all this property parents has key name - if (this.property.getParentNamesArray(childrenProps[0].propertiesName, []).indexOf('') === -1){ + if (property.getParentNamesArray(childrenProps[0].propertiesName, []).indexOf('') === -1){ angular.forEach(childrenProps, (prop:DerivedFEProperty):void => { //Update parent PropertyFEModel with value for each child, including nested props - (<PropertyFEModel>this.property).childPropUpdated(prop); + property.childPropUpdated(prop); + if (prop.isChildOfListOrMap && prop.mapKey !== undefined) { + property.childPropMapKeyUpdated(prop, prop.mapKey, true); + } },this); //grab the cumulative value for the new item from parent PropertyFEModel and assign that value to DerivedFEProp[0] (which is the list or map parent with UUID of the set we just added) - let parentNames = (<PropertyFEModel>this.property).getParentNamesArray(childrenProps[0].propertiesName, []); - childrenProps[0].valueObj = _.get(this.property.valueObj, parentNames.join('.')); + let parentNames = (<PropertyFEModel>property).getParentNamesArray(childrenProps[0].propertiesName, []); + childrenProps[0].valueObj = _.get(property.valueObj, parentNames.join('.'), null); } } } @@ -140,7 +157,7 @@ export class DynamicPropertyComponent { if (this.property.getParentNamesArray(property.propertiesName, []).indexOf('') === -1) {//If one of the parents is empty key -don't save this.property.childPropUpdated(property); this.dataTypeService.checkForCustomBehavior(this.property); - this.valueChanged.emit(this.property.name); + this.emitter.emit(); } } } @@ -153,46 +170,42 @@ export class DynamicPropertyComponent { } } - removeValueFromParent = (item: DerivedFEProperty, target?: any) => { + removeValueFromParent = (item: DerivedFEProperty) => { if (this.property instanceof PropertyFEModel) { - let itemParent = (item.parentName == this.property.name) ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName); + let itemParent = (item.parentName == this.property.name) + ? this.property : this.property.flattenedChildren.find(prop => prop.propertiesName == item.parentName); + if (!itemParent) { + return; + } if (item.derivedDataType == DerivedPropertyType.MAP) { - let oldKey = item.mapKey; - if (target && typeof target.value == 'string') { //allow saving empty string - let replaceKey:string = target.value; - if (!replaceKey) {//prevent delete map key - return; - } - if(Object.keys(itemParent.valueObj).indexOf(replaceKey) > -1){//the key is exists - target.setCustomValidity('This key is already exists.'); - return; - }else { - target.setCustomValidity(''); - _.set(itemParent.valueObj, replaceKey, itemParent.valueObj[oldKey]); - item.mapKey = replaceKey; - //If the map key was empty its valueObj was not updated on its prent property valueObj, and now we should update it. - if(!oldKey && !item.schema.property.isSimpleType){ - //Search this map item children and update these value on parent property valueOBj - let mapKeyFlattenChildren:Array<DerivedFEProperty> = _.filter(this.property.flattenedChildren, (prop:DerivedFEProperty) => { - return _.startsWith(prop.propertiesName, item.propertiesName); - }); - this.updateMapKeyValueOnMainParent(mapKeyFlattenChildren); - } - } - } + const oldKey = item.getActualMapKey(); delete itemParent.valueObj[oldKey]; + if (itemParent instanceof PropertyFEModel) { + delete itemParent.valueObjValidation[oldKey]; + itemParent.valueObjIsValid = itemParent.calculateValueObjIsValid(); + } + this.property.childPropMapKeyUpdated(item, null); // remove map key } else { - let itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName); + const itemIndex: number = this.property.flattenedChildren.filter(prop => prop.parentName == item.parentName).map(prop => prop.propertiesName).indexOf(item.propertiesName); itemParent.valueObj.splice(itemIndex, 1); - } - if (item.mapKey) {//prevent going to BE if user tries to delete map item without key (it was not saved in BE) - if (itemParent instanceof PropertyFEModel) { //direct child - this.valueChanged.emit(this.property.name); - } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc) - this.childValueChanged(itemParent); + if (itemParent instanceof PropertyFEModel) { + itemParent.valueObjValidation.splice(itemIndex, 1); + itemParent.valueObjIsValid = itemParent.calculateValueObjIsValid(); } } + if (itemParent instanceof PropertyFEModel) { //direct child + this.emitter.emit(); + } else { //nested child - need to update parent prop by getting flattened name (recurse through parents and replace map/list keys, etc) + this.childValueChanged(itemParent); + } + } + } + + updateChildKeyInParent(childProp: DerivedFEProperty, newMapKey: string) { + if (this.property instanceof PropertyFEModel) { + this.property.childPropMapKeyUpdated(childProp, newMapKey); + this.emitter.emit(); } } diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html index 933b80f577..ecfa7e7c5e 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.html @@ -48,7 +48,7 @@ [expandedChildId]="property.expandedChildPropertyId" [propertyNameSearchText]="propertyNameSearchText" [readonly]="readonly" - (valueChanged)="propValueChanged(property);" + (propertyChanged)="onPropertyChanged(property)" (expandChild)="property.updateExpandedChildPropertyId($event)" (clickOnPropertyRow)="onClickPropertyInnerRow($event, instanceId)" (checkProperty)="propertyChecked(property, $event)" diff --git a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts index 32bbb1b3a0..093fae1684 100644 --- a/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/properties-table/properties-table.component.ts @@ -39,7 +39,7 @@ export class PropertiesTableComponent { @Input() hasDeclareOption:boolean; @Input() hidePropertyType:boolean; - @Output() valueChanged: EventEmitter<any> = new EventEmitter<any>(); + @Output('propertyChanged') emitter: EventEmitter<PropertyFEModel> = new EventEmitter<PropertyFEModel>(); @Output() selectPropertyRow: EventEmitter<PropertyRowSelectedEvent> = new EventEmitter<PropertyRowSelectedEvent>(); @Output() updateCheckedPropertyCount: EventEmitter<boolean> = new EventEmitter<boolean>();//only for hasDeclareOption @@ -49,8 +49,8 @@ export class PropertiesTableComponent { ngOnInit() { } - propValueChanged = (property) => { - this.valueChanged.emit(property); + onPropertyChanged = (property) => { + this.emitter.emit(property); }; // Click on main row (row of propertyFEModel) diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html index bcd33efc30..6e0f93f750 100644 --- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html +++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.html @@ -22,7 +22,8 @@ </div> </div> <div class="inner-container properties-table-container" *ngIf="selectedReqOrCapOption == 'Capability'" [ngClass]="{'cap-selected':selectedReqOrCapModel}"> - <properties-table *ngIf="selectedReqOrCapModel" + <loader [display]="loadingCapabilityProperties" size="medium" [relative]="true"></loader> + <properties-table *ngIf="selectedReqOrCapModel && !loadingCapabilityProperties" class="properties-table" [readonly]="true" [fePropertiesMap]="capabilityProperties" diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts index ab67dc1850..2e3c21c210 100644 --- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts +++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.component.ts @@ -1,10 +1,10 @@ /** * Created by rc2122 on 9/4/2017. */ +import * as _ from "lodash"; import {Component, EventEmitter, Input, OnInit, Output, SimpleChanges} from '@angular/core'; -import {RadioButtonModel, Match, PropertyModel, InstanceFePropertiesMap, Component as ComponentModel} from "app/models"; +import {RadioButtonModel, PropertyModel, InstanceFePropertiesMap, Component as ComponentModel} from "app/models"; import {Dictionary} from "lodash"; -import {DropdownValue} from "../../ui/form-components/dropdown/ui-element-dropdown.component"; import {ComponentInstanceServiceNg2} from "../../../services/component-instance-services/component-instance.service"; import {PropertiesUtils} from "app/ng2/pages/properties-assignment/services/properties.utils"; import {Requirement} from "../../../../models/requirement"; @@ -33,7 +33,6 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { @Input() selectedReqOrCapModel:RequirementCapabilityModel; @Output() updateSelectedReqOrCap:EventEmitter<RequirementCapabilityModel> = new EventEmitter<RequirementCapabilityModel>(); - @Output() updateCapabilityProperties:EventEmitter<Array<PropertyModel>> = new EventEmitter<Array<PropertyModel>>(); types:Array<string> = []; selectedType:string; @@ -47,10 +46,14 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { displayCapReqListFilterByType:RequirementCapabilityModel[]; capabilityProperties:InstanceFePropertiesMap; + loadingCapabilityProperties:boolean; + + private _loadingCapabilityProperties: Array<Capability>; constructor(private componentInstanceServiceNg2:ComponentInstanceServiceNg2, private propertiesUtils:PropertiesUtils) { this.selectOptions = [new RadioButtonModel(REQUIREMENT, REQUIREMENT), new RadioButtonModel(CAPABILITY, CAPABILITY)]; + this._loadingCapabilityProperties = []; } private initDefaultReqOrCapSelection = (): void => { @@ -87,14 +90,17 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { initCapabilityPropertiesTable = ():void => { if(this.selectedReqOrCapModel instanceof Capability ) { let selectedCapability = <Capability>this.selectedReqOrCapModel; - if(selectedCapability.properties){ + if (selectedCapability.properties && selectedCapability.properties.length) { this.capabilityProperties = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({ CAPABILITY : selectedCapability.properties}, false); + } else { + this.capabilityProperties = null; } } } ngOnChanges(changes:SimpleChanges) { if (changes.selectedReqOrCapModel) { + this.capabilityProperties = null; if (this.selectedReqOrCapModel && this.selectedReqOrCapOption === CAPABILITY) { this.setCapabilityProperties(); } @@ -168,15 +174,27 @@ export class SelectRequirementOrCapabilityComponent implements OnInit { private setCapabilityProperties = ():void => { let selectedCapability = <Capability>this.selectedReqOrCapModel; - if (selectedCapability.properties === undefined) { - this.componentInstanceServiceNg2.getInstanceCapabilityProperties(this.currentComponent, this.componentInstanceId, selectedCapability.type, selectedCapability.name) - .subscribe((response:Array<PropertyModel>) => { - this.capabilityProperties = (response && response.length) ? this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({CAPABILITY : response}, false) : null; - this.updateCapabilityProperties.emit(response); - }, error => {}); - }else{ - this.capabilityProperties = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({CAPABILITY : selectedCapability.properties}, false); - this.updateCapabilityProperties.emit(selectedCapability.properties); + if (!selectedCapability.properties) { + this.loadingCapabilityProperties = true; + if (this._loadingCapabilityProperties.indexOf(selectedCapability) == -1) { + this._loadingCapabilityProperties.push(selectedCapability); + this.componentInstanceServiceNg2.getInstanceCapabilityProperties(this.currentComponent, this.componentInstanceId, selectedCapability) + .subscribe((response: Array<PropertyModel>) => { + if (this.selectedReqOrCapModel === selectedCapability) { + delete this.loadingCapabilityProperties; + } + this.initCapabilityPropertiesTable(); + }, (error) => { + if (this.selectedReqOrCapModel === selectedCapability) { + delete this.loadingCapabilityProperties; + } + }, () => { + this._loadingCapabilityProperties.splice(this._loadingCapabilityProperties.indexOf(selectedCapability), 1); + }); + } + } else { + delete this.loadingCapabilityProperties; + this.initCapabilityPropertiesTable(); } } } diff --git a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts index cb12dea02a..259ae12380 100644 --- a/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts +++ b/catalog-ui/src/app/ng2/components/logic/select-requirement-or-capability/select-requirement-or-capability.module.ts @@ -5,6 +5,7 @@ import {FormElementsModule} from "../../ui/form-components/form-elements.module" import {CommonModule} from "@angular/common"; import {GlobalPipesModule} from "app/ng2/pipes/global-pipes.module"; import {PropertyTableModule} from "../properties-table/property-table.module"; +import {UiElementsModule} from "../../ui/ui-elements.module"; @NgModule({ declarations: [ @@ -15,7 +16,9 @@ import {PropertyTableModule} from "../properties-table/property-table.module"; FormsModule, FormElementsModule, GlobalPipesModule, - PropertyTableModule], + UiElementsModule, + PropertyTableModule + ], exports: [SelectRequirementOrCapabilityComponent], providers: [] diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html new file mode 100644 index 0000000000..72e083534c --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.html @@ -0,0 +1,11 @@ +<div class="service-path-selector"> + <label>Service Paths:</label> + <ui-element-dropdown + class="path-dropdown" + data-tests-id="service-path-selector" + [readonly]="dropdownOptions.length < 3" + [(value)]="selectedPathId" + [values]="dropdownOptions" + (valueChange)="onSelectPath()"> + </ui-element-dropdown> +</div> diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less new file mode 100644 index 0000000000..f3cb4a3c34 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.less @@ -0,0 +1,24 @@ +@import './../../../../../assets/styles/variables.less'; +.service-path-selector { + margin: 10px 35px 10px 0; + display: flex; + font-size: 12px; + + /deep/ .path-dropdown { + width: 150px; + select { + font-size: 14px; + font-family: @font-opensans-regular; + padding: 4px 10px; + } + } + + label { + margin-right: 10px; + align-self: center; + font-size: 14px; + font-family: @font-opensans-regular; + font-weight: normal; + margin-bottom: initial; + } +} diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts new file mode 100644 index 0000000000..be9966acef --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.component.ts @@ -0,0 +1,134 @@ +import * as _ from "lodash"; +import {Component, Input, KeyValueDiffer, IterableDiffers, KeyValueDiffers, DoCheck} from '@angular/core'; +import {Service} from "app/models/components/service"; +import {TranslateService} from "app/ng2/shared/translator/translate.service"; +import {ForwardingPath} from "app/models/forwarding-path"; +import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; + +@Component({ + selector: 'service-path-selector', + templateUrl: './service-path-selector.component.html', + styleUrls:['service-path-selector.component.less'], + providers: [TranslateService] +}) + +export class ServicePathSelectorComponent implements DoCheck { + + defaultSelectedId: string; + hideAllValue: string; + hideAllId: string = '0'; + showAllValue: string; + showAllId: string = '1'; + + paths: Array<ForwardingPath> = []; + dropdownOptions: Array<DropdownValue>; + differ: KeyValueDiffer; + + @Input() service: Service; + @Input() drawPath: Function; + @Input() deletePaths: Function; + @Input() selectedPathId: string; + + constructor(private differs: KeyValueDiffers, private translateService: TranslateService) { + + this.defaultSelectedId = this.hideAllId; + this.convertPathsToDropdownOptions(); + + this.translateService.languageChangedObservable.subscribe(lang => { + this.hideAllValue = this.translateService.translate("SERVICE_PATH_SELECTOR_HIDE_ALL_VALUE"); + this.showAllValue = this.translateService.translate("SERVICE_PATH_SELECTOR_SHOW_ALL_VALUE"); + this.convertPathsToDropdownOptions(); + }); + + } + + ngOnInit(): void { + + this.selectedPathId = this.defaultSelectedId; + this.differ = this.differs.find(this.service.forwardingPaths).create(null); + + } + + ngDoCheck(): void { + + const pathsChanged = this.differ.diff(this.service.forwardingPaths); + + if (pathsChanged) { + let oldPaths = _.cloneDeep(this.paths); + this.populatePathsFromService(); + + if (!(_.isEqual(oldPaths, this.paths))) { + this.convertPathsToDropdownOptions(); + + let temp = this.selectedPathId; + this.selectedPathId = '-1'; + + setTimeout(() => { + this.selectedPathId = temp; + this.onSelectPath(); + }, 0); + } + } + + } + + populatePathsFromService(): void { + + this.paths = []; + let {forwardingPaths} = this.service; + + _.forEach(forwardingPaths, path => { + this.paths.push(path); + }); + this.paths.sort((a:ForwardingPath, b:ForwardingPath)=> { + return a.name.localeCompare(b.name); + }); + + } + + convertPathsToDropdownOptions(): void { + + let result = [ + new DropdownValue(this.hideAllId, this.hideAllValue), + new DropdownValue(this.showAllId, this.showAllValue) + ]; + + _.forEach(this.paths, (value: ForwardingPath) => { + result[result.length] = new DropdownValue(value.uniqueId, value.name); + }); + + this.dropdownOptions = result; + + } + + onSelectPath = (): void => { + + if (this.selectedPathId !== '-1') { + this.deletePaths(); + + switch (this.selectedPathId) { + case this.hideAllId: + break; + + case this.showAllId: + _.forEach(this.paths, path => + this.drawPath(path) + ); + break; + + default: + let path = this.paths.find(path => + path.uniqueId === this.selectedPathId + ); + if (!path) { + this.selectedPathId = this.defaultSelectedId; + this.onSelectPath(); // currently does nothing in default case, but if one day it does, we want the selection to behave accordingly. + break; + } + this.drawPath(path); + break; + } + } + + } +} diff --git a/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts new file mode 100644 index 0000000000..c07061ce9a --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path-selector/service-path-selector.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServicePathSelectorComponent} from "./service-path-selector.component"; +import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; + +@NgModule({ + declarations: [ + ServicePathSelectorComponent + ], + imports: [ + CommonModule, + UiElementsModule + ], + exports: [], + entryComponents: [ + ServicePathSelectorComponent + ], + providers: [] +}) +export class ServicePathSelectorModule { +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html new file mode 100644 index 0000000000..ff7902e2b9 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.html @@ -0,0 +1,22 @@ +<div class='service-path'> + <button class='zoom-icons create-path-button' data-tests-id="pathsMenuBtn" (click)="showServicePathMenu = !showServicePathMenu">...</button> + <div class="service-path-menu" + *ngIf="showServicePathMenu" > + <div > + <ul> + <li><div class="hand" (click)="onCreateServicePath()" data-tests-id="createPathMenuItem"> + Create Service Path + </div></li> + <li><div class="hand" (click)="onListServicePath()" data-tests-id="pathsListMenuItem"> + Service Paths List + </div></li> + </ul> + </div> + </div> + <!-- TODO - ask Orit about positioning issues and styling issues --> + <!-- + <menu-list [open]="showServicePathMenu" [position]="menuPos" > + <menu-item [action]="onCreateServicePath">Create Path</menu-item> + <menu-item [action]="onListServicePath">Paths' List</menu-item> + </menu-list> --> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less new file mode 100644 index 0000000000..777b206714 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.less @@ -0,0 +1,51 @@ +//@import 'src/assets/styles/variables.less'; +@import './../../../../../assets/styles/variables.less'; +.service-path { + position: relative; + .create-path-button{ + &:extend(.search-bar-button); + width: 30px; + height: 30px; + &:hover { + color: @main_color_a; + } + &:active { + background: @main_color_a; + color: @main_color_p; + } + &:focus { + outline: none; + } + } + .service-path-menu { + border: 1px solid @main_color_o; + border-radius: 0 0 2px 2px; + border-top-color: @main_color_a; + border-top-width: 3px; + + box-sizing: border-box; + box-shadow: 0 2px 4px 0 rgba(0,0,0,0.30); + + background-color: @main_color_p; + + padding: 5px 0; + right: 34px; + position: absolute; + top: 10px; + width: 150px; + font-size: 13px; + font-family: @font-opensans-regular; + + li { + color: @main_color_m; + padding: 0 10px; + line-height: 20px; + &:hover { + cursor: pointer; + color: @main_color_a; + background-color: fade(@main_color_a, 5%); + } + } + + } +} diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts new file mode 100644 index 0000000000..4a6209fb6f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.component.ts @@ -0,0 +1,83 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {Component, Input, ComponentRef} from '@angular/core'; +import {ModalService} from 'app/ng2/services/modal.service'; +import {ModalModel, ButtonModel} from 'app/models'; +import {ServicePathCreatorComponent} from 'app/ng2/pages/service-path-creator/service-path-creator.component'; +import {ModalComponent} from 'app/ng2/components/ui/modal/modal.component'; +import ServicePathsListComponent from "app/ng2/pages/service-paths-list/service-paths-list.component"; +import {Service} from "app/models/components/service"; + +@Component({ + selector: 'service-path', + templateUrl: './service-path.component.html', + styleUrls: ['service-path.component.less'], + providers: [ModalService] +}) + +export class ServicePathComponent { + showServicePathMenu: boolean = false; + modalInstance: ComponentRef<ModalComponent>; + @Input() service: Service; + @Input() onCreate: Function; + @Input() onSave: Function; + + constructor(private ModalServiceNg2: ModalService) {} + + onCreateServicePath = ():void => { + this.showServicePathMenu = false; + let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline white', this.ModalServiceNg2.closeCurrentModal); + let saveButton: ButtonModel = new ButtonModel('Create', 'blue', this.createPath, this.getDisabled ); + let modalModel: ModalModel = new ModalModel('l', 'Create Service Path', '', [saveButton, cancelButton], 'standard', true); + this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); + this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathCreatorComponent, {service: this.service}); + this.modalInstance.instance.open(); + }; + + onListServicePath = ():void => { + this.showServicePathMenu = false; + let cancelButton: ButtonModel = new ButtonModel('Close', 'outline white', this.ModalServiceNg2.closeCurrentModal); + let modalModel: ModalModel = new ModalModel('md', 'Service Paths List','', [cancelButton], 'standard', true); + this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); + this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathsListComponent, {service: this.service, + onCreateServicePath: this.onCreateServicePath, onEditServicePath: this.onEditServicePath}); + this.modalInstance.instance.open(); + }; + + createPath = ():void => { + this.onCreate(this.modalInstance.instance.dynamicContent.instance.createServicePathData()); + this.ModalServiceNg2.closeCurrentModal(); + }; + + onEditServicePath = (id:string):void => { + let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline white', this.ModalServiceNg2.closeCurrentModal); + let saveButton: ButtonModel = new ButtonModel('Save', 'blue', this.createPath, this.getDisabled ); + let modalModel: ModalModel = new ModalModel('l', 'Edit Path', '', [saveButton, cancelButton]); + this.modalInstance = this.ModalServiceNg2.createCustomModal(modalModel); + this.ModalServiceNg2.addDynamicContentToModal(this.modalInstance, ServicePathCreatorComponent, {service: this.service, pathId: id}); + this.modalInstance.instance.open(); + }; + + getDisabled = ():boolean => { + return !this.modalInstance.instance.dynamicContent.instance.checkFormValidForSubmit(); + }; +} + diff --git a/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts new file mode 100644 index 0000000000..96af247086 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/logic/service-path/service-path.module.ts @@ -0,0 +1,39 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServicePathComponent} from "./service-path.component"; +import {UiElementsModule} from "app/ng2/components/ui/ui-elements.module"; + +@NgModule({ + declarations: [ + ServicePathComponent + ], + imports: [CommonModule, + UiElementsModule], + exports: [], + entryComponents: [ + ServicePathComponent + ], + providers: [] +}) +export class ServicePathModule { +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html new file mode 100644 index 0000000000..e279e3f704 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.html @@ -0,0 +1,11 @@ +<div class="sdc-canvas-zone {{class}}-zone" [class.minimized]="minifyZone" [class.hidden]="!showZone"> + <div class="sdc-canvas-zone__header" (click)="unminifyZone()" > + <div class="sdc-canvas-zone__title">{{title}} + <span class="sdc-canvas-zone__counter">{{count}}</span> + </div> + <span class="sdc-canvas-zone__state-button">–</span> + </div> + <div class="sdc-canvas-zone__container"> + <ng-content></ng-content> + </div> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less new file mode 100644 index 0000000000..3e77c5ca3b --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.less @@ -0,0 +1,62 @@ +.sdc-canvas-zone { + width: 280px; + max-height:186px; + display:flex; + flex-direction:column; + align-self: flex-end; + color:white; + font-family:OpenSans-Regular, sans-serif; + transition: width .2s ease-in-out, max-height .2s ease-in-out .1s; + position:relative; + bottom:0px; + + .sdc-canvas-zone__header { + background: #5A5A5A; + border-radius: 2px 2px 0 0; + padding: 5px 10px; + display:flex; + justify-content: space-between; + font-size: 14px; + text-transform:uppercase; + .sdc-canvas-zone__state-button { + font-weight:bold; + cursor:pointer; + } + } + + .sdc-canvas-zone__container { + padding:5px; + background-color: #5A5A5A; + opacity:0.9; + flex: 1; + display:flex; + flex-direction: row; + align-items: flex-start; + flex-wrap:wrap; + overflow-y:auto; + min-height: 80px; + max-height: 170px; + } + + + &.minimized { + max-height:30px; + width:120px; + cursor:pointer; + + .sdc-canvas-zone__state-button { + display:none; + } + .sdc-canvas-zone__container { + flex: 0 0 0; + min-height: 0; + padding: 0; + overflow-y:hidden; + transition: min-height .2s ease-in-out .2s; + transition: padding .1s ease-in-out 0s; + } + } + &.hidden { + display:none; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts new file mode 100644 index 0000000000..7e60cb37a6 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-container.component.ts @@ -0,0 +1,25 @@ +import { Component, Input, Output, ViewEncapsulation, EventEmitter } from '@angular/core'; +import { EventListenerService } from 'app/services'; +import { GRAPH_EVENTS } from 'app/utils'; + +@Component({ + selector: 'zone-container', + templateUrl: './zone-container.component.html', + styleUrls: ['./zone-container.component.less'], + encapsulation: ViewEncapsulation.None +}) + +export class ZoneContainerComponent { + @Input() title:string; + @Input() class:string; + @Input() count:number; + @Input() showZone:boolean; + @Input() minifyZone:boolean; + constructor(private eventListenerService:EventListenerService) {} + + private unminifyZone = () => { + this.minifyZone = !this.minifyZone; + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_SIZE_CHANGE); + } + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html new file mode 100644 index 0000000000..d36b7aec6f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.html @@ -0,0 +1,8 @@ +<div class="zone-child mode-{{config.mode}}" [class.locked]="activeInstanceMode > MODE.HOVER" + (mouseenter)="setMode(MODE.HOVER)" (mouseleave)="setMode(MODE.NONE)" (click)="setMode(MODE.SELECTED)"> + <div class="zone-child__handle" (click)="setMode(MODE.TAG, $event)">+</div> + <div class="zone-child__body"> + <div class="zone-child__body-content">{{config.count || defaultIconText}}</div> + </div> + <div class="zone-child__name">{{config.name}}</div> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less new file mode 100644 index 0000000000..a1d56df96e --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.less @@ -0,0 +1,110 @@ +.zone-child { + position:relative; + width:76px; + margin:5px; + + .zone-child__handle { + display:none; + position:absolute; + right:4px; + top:10px; + width:22px; + height:22px; + cursor:pointer; + border: solid white 1px; + border-radius: 2px; + text-align: center; + font-weight:bold; + } + + .zone-child__body { + margin:0 auto; + width:43px; + height:43px; + display:flex; + padding:3px; + + } + + .zone-child__body-content { + border-radius: 2px; + flex:1; + color:white; + font-size:18px; + text-align:center; + display:flex; + align-items: center; + justify-content: center; + } + + .zone-child__name { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + text-align:center; + } + /* Dynamic classes below */ + + &.mode-1, &.mode-2, &.mode-3 { //hover, selected, tag + .zone-child__body { + border:solid 2px; + border-radius: 2px; + padding:2px; + cursor:pointer; + } + .zone-child__handle{ + display:block; + cursor:pointer; + } + } + + &.locked { + cursor: default; + } + + // &:not(.locked):hover .zone-child__handle{ + // display:block; + // } + .zone-child__body { + cursor: default; + } + &.mode-3 .zone-child__handle { + width:24px; + height:24px; + right:3px; + top:9px; + display:block; + background-image: linear-gradient(-140deg, #009E98 0%, #97D648 100%); + border: 2px solid #FFFFFF; + border-radius: 2px; + box-shadow: inset 2px -2px 3px 0 #007A3E; + cursor: pointer; + } + +} +.sdc-canvas-zone.group-zone { + .zone-child__handle { + background-color:#009FDB; + } + .zone-child__body { + border-color:#009FDB; + } + .zone-child__body-content { + background: #009FDB; + } +} + +.sdc-canvas-zone.policy-zone { + .zone-child__handle { + background-color:#0568AE; + } + .zone-child__body { + border-color:#1287D9; + .zone-child__body-content { + background: #1287D9; + } + } + .zone-child__body-content { + background: #0568AE; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts new file mode 100644 index 0000000000..8057ae908a --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/canvas-zone/zone-instance/zone-instance.component.ts @@ -0,0 +1,28 @@ +import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core'; +import { ZoneInstanceConfig, ZoneInstanceMode } from 'app/models/graph/zones/zone-child'; + +@Component({ + selector: 'zone-instance', + templateUrl: './zone-instance.component.html', + styleUrls: ['./zone-instance.component.less'], + encapsulation: ViewEncapsulation.None +}) +export class ZoneInstanceComponent { + + @Input() config:ZoneInstanceConfig; + @Input() defaultIconText:string; + @Input() isActive:boolean; + @Input() activeInstanceMode: ZoneInstanceMode; + @Output() modeChange: EventEmitter<any> = new EventEmitter<any>(); + private MODE = ZoneInstanceMode; + + private setMode = (mode:ZoneInstanceMode, event?:any):void => { + if(!this.isActive || this.isActive && mode == ZoneInstanceMode.TAG){ //when active, do not allow hover/select mode toggling + this.modeChange.emit({newMode: mode, instance: this.config}); + } + if(event){ + event.stopPropagation(); + } + } + +}
\ 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 53d1590b1c..d1e68f3088 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 @@ -18,14 +18,25 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Component, Compiler, EventEmitter, ViewContainerRef, ViewChild, Input, Output, ElementRef, ComponentRef, ComponentFactoryResolver } from '@angular/core' import {ValidationConfiguration} from "app/models"; +import {IUiElementChangeEvent} from "../form-components/ui-element-base.component"; import {UiElementInputComponent} from "../form-components/input/ui-element-input.component"; import {UiElementPopoverInputComponent} from "../form-components/popover-input/ui-element-popover-input.component"; import {UiElementIntegerInputComponent} from "../form-components/integer-input/ui-element-integer-input.component"; import {UiElementDropDownComponent, DropdownValue} from "../form-components/dropdown/ui-element-dropdown.component"; import {PROPERTY_DATA} from "../../../../utils/constants"; +enum DynamicElementComponentCreatorIdentifier { + STRING, + INTEGER, + FLOAT, + BOOLEAN, + SUBNETPOOLID, + DEFAULT +} + @Component({ selector: 'dynamic-element', template: `<div #target></div>`, @@ -44,16 +55,14 @@ export class DynamicElementComponent { @Input() name: string; @Input() readonly:boolean; @Input() path:string;//optional param. used only for for subnetpoolid type - value:any; - // Two way binding for value (need to write the "Change" word like this) - @Output('valueChange') emitter: EventEmitter<string> = new EventEmitter<any>(); - @Input('value') set setValueValue(value) { - this.value = value; - } + @Input() value: any; + @Output() valueChange: EventEmitter<any> = new EventEmitter<any>(); + @Output('elementChanged') emitter: EventEmitter<IUiElementChangeEvent> = new EventEmitter<IUiElementChangeEvent>(); cmpRef: ComponentRef<any>; private isViewInitialized: boolean = false; + private elementCreatorIdentifier: DynamicElementComponentCreatorIdentifier; validation = ValidationConfiguration.validation; constructor( @@ -66,29 +75,72 @@ export class DynamicElementComponent { if (!this.isViewInitialized) { return; } - if (this.cmpRef) { - this.cmpRef.destroy(); - } - // Factory to create component based on type or peroperty name. + // Factory to create component based on type or other property attributes. + const prevElementCreatorIdentifier: DynamicElementComponentCreatorIdentifier = this.elementCreatorIdentifier; switch(true) { case this.path && this.path.toUpperCase().indexOf("SUBNETPOOLID") !== -1: + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.SUBNETPOOLID; + break; + case this.type === 'integer': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.INTEGER; + break; + case this.type === 'float': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.FLOAT; + break; + case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1: + case this.type === 'string': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.STRING; + break; + case this.type === 'boolean': + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.BOOLEAN; + break; + default: + this.elementCreatorIdentifier = DynamicElementComponentCreatorIdentifier.DEFAULT; + } + + // In case the dynamic element creator is changed, then destroy old and build new. + if (this.elementCreatorIdentifier !== prevElementCreatorIdentifier) { + if (this.cmpRef) { + this.cmpRef.destroy(); + } + this.createComponentByIdentifier(); + } + + // Update attributes in base element class + if (this.cmpRef) { + this.cmpRef.instance.name = this.name; + this.cmpRef.instance.type = this.type; + this.cmpRef.instance.value = this.value; + this.cmpRef.instance.readonly = this.readonly; + } + } + + createComponentByIdentifier() { + switch(this.elementCreatorIdentifier) { + case DynamicElementComponentCreatorIdentifier.SUBNETPOOLID: if(this.name.toUpperCase().indexOf("SUBNETPOOLID") == -1){//if it's an item of subnetpoolid list get the parent name let pathArray = this.path.split("#"); this.name = pathArray[pathArray.length - 2]; } this.createComponent(UiElementPopoverInputComponent); break; - case this.type == 'integer': + + case DynamicElementComponentCreatorIdentifier.INTEGER: this.createComponent(UiElementIntegerInputComponent); this.cmpRef.instance.pattern = this.validation.validationPatterns.integer; break; - case PROPERTY_DATA.SCALAR_TYPES.indexOf(this.type) > -1: - case this.type == 'string': + + case DynamicElementComponentCreatorIdentifier.FLOAT: + this.createComponent(UiElementIntegerInputComponent); + this.cmpRef.instance.pattern = /^[-+]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?$/; + break; + + case DynamicElementComponentCreatorIdentifier.STRING: this.createComponent(UiElementInputComponent); break; - case this.type == 'boolean': + case DynamicElementComponentCreatorIdentifier.BOOLEAN: this.createComponent(UiElementDropDownComponent); // Build drop down values @@ -96,25 +148,20 @@ 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); + } break; + + case DynamicElementComponentCreatorIdentifier.DEFAULT: default: this.createComponent(UiElementInputComponent); console.log("ERROR: No ui component to handle type: " + this.type); } - // Additional attributes in base element class - if (this.cmpRef) { - this.cmpRef.instance.name = this.name; - this.cmpRef.instance.type = this.type; - this.cmpRef.instance.value = this.value; - this.cmpRef.instance.readonly = this.readonly; - } - // Subscribe to change event of of ui-element-basic and fire event to change the value - this.cmpRef.instance.baseEmitter.subscribe((value):void => { - this.emitter.emit(value) - }); - + this.cmpRef.instance.baseEmitter.subscribe((event) => { this.emitter.emit(event); }); + this.cmpRef.instance.valueChange.subscribe((event) => { this.valueChange.emit(event); }); } createComponent(ComponentToCreate:any):void { diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html index c6b8384183..805e5ac295 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html +++ b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.html @@ -1,3 +1,3 @@ -<select name='{{name}}' [(ngModel)]="value" (change)="onSave()" [ngClass]="{'disabled':readonly}" data-tests-id="SelectType"> +<select name='{{name}}' [(ngModel)]="value" (change)="onChange()" [ngClass]="{'disabled':readonly}" data-tests-id="SelectType"> <option *ngFor="let ddvalue of values" [ngValue]="ddvalue.label != undefined ? ddvalue.value : ddvalue">{{ddvalue.label||ddvalue}}</option> </select> diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts index 5abf32c61b..03a1fc6040 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component.ts @@ -43,9 +43,4 @@ export class UiElementDropDownComponent extends UiElementBase implements UiEleme constructor() { super(); } - - onSave() { - this.baseEmitter.emit(this.value); - } - } diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html index b7d7c859c7..057e731ada 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html +++ b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.html @@ -4,7 +4,7 @@ type="text" [name]="name" [(ngModel)]="value" - (change)="onSave()" + (input)="onChange()" [attr.maxlength]="validation.propertyValue.max" [attr.minlength]="validation.propertyValue.min" [pattern]="pattern" diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts index fb3b3db859..ce231e76e8 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/input/ui-element-input.component.ts @@ -32,10 +32,4 @@ export class UiElementInputComponent extends UiElementBase implements UiElementB super(); this.pattern = this.validation.validationPatterns.comment; } - - onSave() { - if (!this.control.invalid){ - this.baseEmitter.emit(this.value); - } - } } diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html index 9fbc9e1094..e1555e87fd 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html +++ b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.html @@ -4,7 +4,7 @@ type="text" [name]="name" [(ngModel)]="value" - (change)="onSave()" + (input)="onChange()" [attr.maxlength]="validation.propertyValue.max" [attr.minlength]="validation.propertyValue.min" [pattern]="pattern" diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts index 1667f4393d..9aa4b5872c 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/integer-input/ui-element-integer-input.component.ts @@ -33,9 +33,8 @@ export class UiElementIntegerInputComponent extends UiElementBase implements UiE //this.pattern = this.validation.validationPatterns.comment; } - onSave() { - if (!this.control.invalid){ - this.baseEmitter.emit(this.value ? JSON.parse(this.value) : this.value); - } + onChange() { + this.value = this.control.valid && this.value ? JSON.parse(this.value) : this.value; + super.onChange(); } } diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts index 61688df3f0..525cd1742c 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/popover-input/ui-element-popover-input.component.ts @@ -35,17 +35,10 @@ export class UiElementPopoverInputComponent extends UiElementBase implements UiE saveButton: ButtonModel; buttonsArray: ButtonsModelMap; - onSave = ():void => { - if (!this.control.invalid){ - this.baseEmitter.emit(this.value); - this.popoverContentComponent.hide(); - } - } - constructor() { super(); // Create Save button and insert to buttons map - this.saveButton = new ButtonModel('save', 'blue', this.onSave); + this.saveButton = new ButtonModel('save', 'blue', this.onChange); this.buttonsArray = { 'test': this.saveButton }; // Define the regex pattern for this controller diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts index 0f80e2ad44..3e8c3b5813 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/radio-buttons/radio-buttons.component.ts @@ -12,10 +12,6 @@ import {UiElementBaseInterface, UiElementBase} from "../ui-element-base.componen }) export class RadioButtonComponent extends UiElementBase implements UiElementBaseInterface { - onSave() { - this.baseEmitter.emit(this.value); - } - @Input() options:Array<RadioButtonModel>; @Input() readonly:boolean; @Input() direction:string = 'vertical'; //get 'horizontal' | 'vertical' @@ -23,7 +19,7 @@ export class RadioButtonComponent extends UiElementBase implements UiElementBas select(value:any) { this.value = value; - this.baseEmitter.emit(this.value); + this.onChange(); } } diff --git a/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts b/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts index ae2013ff70..b4e9e7d36a 100644 --- a/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/form-components/ui-element-base.component.ts @@ -23,7 +23,12 @@ import { ValidationConfiguration } from "app/models"; import { FormControl, Validators } from '@angular/forms'; export interface UiElementBaseInterface { - onSave(); + onChange(); +} + +export interface IUiElementChangeEvent { + value: any; + isValid: boolean; } @Component({ @@ -35,21 +40,29 @@ export class UiElementBase { protected validation = ValidationConfiguration.validation; protected control: FormControl; - // Two way binding for value (need to write the "Change" word like this) - @Output('valueChange') baseEmitter: EventEmitter<string> = new EventEmitter<any>(); - @Input('value') set setValueValue(value) { - this.value = value; - } + @Input() value: any; + @Output() valueChange: EventEmitter<any> = new EventEmitter<any>(); + @Output('elementChanged') baseEmitter: EventEmitter<IUiElementChangeEvent> = new EventEmitter<IUiElementChangeEvent>(); @Input() name: string; @Input() type: string; - @Input() value: any; @Input() pattern: any; @Input() readonly:boolean; constructor() { //this.control = new FormControl('', [Validators.required]); this.control = new FormControl('', []); + + this.baseEmitter.subscribe((changeEvent: IUiElementChangeEvent) => { + this.valueChange.emit(changeEvent.value); + }) + } + + onChange() { + this.baseEmitter.emit({ + value: this.value, + isValid: this.control.valid + }); } } diff --git a/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts b/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts index f66aa551e2..585c36660e 100644 --- a/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/loader/loader.component.ts @@ -74,10 +74,10 @@ export class LoaderComponent { if (this.relative === true) { let parentElement = this.viewContainerRef.element.nativeElement.parentElement; this.offset = { - left: (parentElement.offsetLeft) ? parentElement.offsetLeft + "px" : undefined, - top: (parentElement.offsetTop) ? parentElement.offsetTop + "px" : undefined, - width: (parentElement.offsetWidth) ? parentElement.offsetWidth + "px" : undefined, - height: (parentElement.offsetHeight) ? parentElement.offsetHeight + "px" : undefined + left: (parentElement.offsetLeft !== undefined) ? parentElement.offsetLeft + "px" : undefined, + top: (parentElement.offsetTop !== undefined) ? parentElement.offsetTop + "px" : undefined, + width: (parentElement.offsetWidth !== undefined) ? parentElement.offsetWidth + "px" : undefined, + height: (parentElement.offsetHeight !== undefined) ? parentElement.offsetHeight + "px" : undefined }; } this.isVisible = true; diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html index 9a0fdf6bf4..6fc55d19e7 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.html @@ -1,6 +1,14 @@ <div class="custom-modal {{input.size}}"> - <div class="ng2-modal-content"> - <div class="ng2-modal-header modal-type-{{input.type}}"> + <div class="ng2-modal-content" + ngDraggable + [ngDraggable]="input.isMovable" + [handle]="ModalHandle" + [bounds]="ModalBounds" + [inBounds]="true" + [preventDefaultEvent]="false"> + <div #ModalHandle + class="ng2-modal-header modal-type-{{input.type}}" + [ngClass]="{'movable': input.isMovable}"> <span class="title">{{ input.title }}</span> <span class="close-button" (click)="close()"></span> </div> @@ -18,4 +26,4 @@ </div> </div> </div> -<div class="modal-background"></div> +<div #ModalBounds class="modal-background" [ngClass]="{'transparent': input.isMovable}"></div> diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less index fac1ae74a8..03b2a70667 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.less @@ -12,7 +12,7 @@ bottom: 0; left: 0; z-index: 15007; - overflow: auto; + overflow: visible; margin: auto; display: flex; align-items: center; @@ -22,6 +22,8 @@ width: 100%; box-shadow: 0 5px 15px rgba(0,0,0,.5); border-radius: 4px; + + .ng2-modal-body{ padding: 20px; } @@ -29,19 +31,28 @@ .ng2-modal-header{ .m_18_r; font-weight: bold; + -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; + height: 50px; line-height: 50px; + margin: 0px 20px; + display: -webkit-box; display: -ms-flexbox; display: flex; + text-align: left; -webkit-box-align: center; -ms-flex-align: center; align-items: center; - margin: 0px 20px; + + &.ng-draggable { + cursor: move; + user-select: none; + } &.modal-type-standard { border-bottom: solid 3px @main_color_a; @@ -101,9 +112,14 @@ background-color: #000; opacity: 0.5; z-index: 900; + + &.transparent { + background-color: transparent; + } } + .xl { width: 1200px; } diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts index 89db8d1140..777e9bdc06 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.component.ts @@ -33,6 +33,7 @@ import { ButtonsModelMap, ModalModel } from 'app/models'; }) export class ModalComponent implements OnInit, OnDestroy { + @Input() isMovable: boolean; @Input() input: ModalModel; @Input() dynamicContent: any; @ViewChild('dynamicContentContainer', { read: ViewContainerRef }) dynamicContentContainer: ViewContainerRef; //Allows for custom component as body instead of simple message. See ModalService.createActionModal for implementation details, and HttpService's catchError() for example. diff --git a/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts b/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts index c38e60194b..2999528d13 100644 --- a/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts +++ b/catalog-ui/src/app/ng2/components/ui/modal/modal.module.ts @@ -1,5 +1,6 @@ import { NgModule } from "@angular/core"; import { CommonModule } from '@angular/common'; +import { AngularDraggableModule } from 'angular2-draggable'; import { ModalService } from 'app/ng2/services/modal.service'; import { ErrorMessageComponent } from "./error-message/error-message.component"; import {ModalComponent} from "./modal.component"; @@ -9,7 +10,7 @@ import {ModalComponent} from "./modal.component"; ModalComponent, ErrorMessageComponent ], - imports: [CommonModule], + imports: [CommonModule, AngularDraggableModule], exports: [ModalComponent, ErrorMessageComponent], entryComponents: [ //need to add anything that will be dynamically created ModalComponent, diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html new file mode 100644 index 0000000000..2a776d444f --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.html @@ -0,0 +1,4 @@ +<div class="palette-animation-wrapper" [style.top]="from.y + 50 + 'px'" [style.left]="from.x + 'px'" [style.transform]="transformStyle" [class.hidden]="!visible" + (transitionend)="animationComplete()"> +<div class="medium small sprite-resource-icons sprite-{{iconName}}-icons {{iconName}}" ></div> +</div> diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less new file mode 100644 index 0000000000..54f04189c0 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.less @@ -0,0 +1,5 @@ +.palette-animation-wrapper{ + position: absolute; + z-index: 100; + transition: all 2s ease-in-out; +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts new file mode 100644 index 0000000000..609a1fc5e1 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.component.ts @@ -0,0 +1,64 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import {Component, Input } from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import { setTimeout } from 'core-js/library/web/timers'; +import { EventListenerService } from 'app/services'; +import { GRAPH_EVENTS } from 'app/utils'; +import { Point } from 'app/models'; + + + +@Component({ + selector: 'palette-animation', + templateUrl: './palette-animation.component.html', + styleUrls:['./palette-animation.component.less'], +}) + +export class PaletteAnimationComponent { + + @Input() from : Point; + @Input() to : Point; + @Input() iconName : string; + @Input() data : any; + + public animation; + private visible:boolean = false; + private transformStyle:string = ""; + + + constructor(private eventListenerService:EventListenerService) {} + + public runAnimation() { + this.visible = true; + let positionDiff:Point = new Point(this.to.x - this.from.x, this.to.y - this.from.y); + setTimeout(()=>{ + this.transformStyle = 'translate('+ positionDiff.x + 'px,' + positionDiff.y +'px)'; + }, 0); + }; + + public animationComplete = (e) => { + this.visible = false; + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_FINISH_ANIMATION_ZONE); + }; + + +} diff --git a/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts new file mode 100644 index 0000000000..8674571138 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-animation/palette-animation.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { PaletteAnimationComponent } from "./palette-animation.component"; + + +@NgModule({ + declarations: [ + PaletteAnimationComponent + ], + imports: [ CommonModule ], + exports: [ PaletteAnimationComponent ] +}) + +export class PaletteAnimationModule { + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html new file mode 100644 index 0000000000..ed172bf2b0 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.html @@ -0,0 +1,9 @@ +<div class="popup-panel" [ngClass]="{'hide':!isShowPanel}" [style.left]="popupPanelPosition.x + 'px'" [style.top]="popupPanelPosition.y + 'px'" + (mousedown)="addZoneInstance()" + (mouseenter)="onMouseEnter()" + (mouseleave)="onMouseLeave()"> + <div class="popup-panel-group"> + <div class="popup-panel-plus">+</div> + <div class="popup-panel-title">{{panelTitle}}</div> + </div> +</div> diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less new file mode 100644 index 0000000000..24f0485e76 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.less @@ -0,0 +1,37 @@ +.popup-panel { + position: absolute; + display: inline-block; + background-color: white; + border: solid 1px #d2d2d2; + border-top: solid 3px #13a7df; + left: 208px; top: 0px; + width: 140px; + height: 40px; + z-index: 10000; + + &:hover { + background-color: whitesmoke; + } + + .popup-panel-group { + padding-left: 8px; + padding-top: 8px; + cursor: pointer; + + .popup-panel-plus { + border-radius: 50%; + color: white; + background-color: #13a7df; + width: 20px; + text-align: center; + display: inline-block; + } + + .popup-panel-title { + padding-left: 10px; + display: inline-block; + } + + } + +} diff --git a/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts new file mode 100644 index 0000000000..d30d5f6178 --- /dev/null +++ b/catalog-ui/src/app/ng2/components/ui/palette-popup-panel/palette-popup-panel.component.ts @@ -0,0 +1,87 @@ +import {Component, OnInit} from '@angular/core'; +import {GRAPH_EVENTS} from "app/utils"; +import {LeftPaletteComponent, Point} from "app/models"; +import {EventListenerService} from "app/services"; +import {LeftPaletteMetadataTypes} from "../../../../models/components/displayComponent"; + +@Component({ + selector: 'app-palette-popup-panel', + templateUrl: './palette-popup-panel.component.html', + styleUrls: [ './palette-popup-panel.component.less' ], +}) +export class PalettePopupPanelComponent implements OnInit { + + public panelTitle: string; + public isShowPanel: boolean; + private component: Component; + private displayComponent: LeftPaletteComponent; + private popupPanelPosition:Point = new Point(0,0); + + constructor(private eventListenerService: EventListenerService) { + this.isShowPanel = false; + } + + ngOnInit() { + this.registerObserverCallbacks(); + } + + public onMouseEnter() { + this.isShowPanel = true; + } + + public onMouseLeave() { + this.isShowPanel = false; + } + + public addZoneInstance(): void { + if(this.displayComponent) { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ADD_COMPONENT_INSTANCE_ZONE_START, this.component, this.displayComponent, this.popupPanelPosition); + } + } + + private registerObserverCallbacks() { + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_SHOW_POPUP_PANEL, + (component: Component, displayComponent: LeftPaletteComponent, sectionElem: HTMLElement) => { + + this.component = component; + this.showPopupPanel(displayComponent, sectionElem); + }); + + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HIDE_POPUP_PANEL, () => this.hidePopupPanel()); + } + + private getPopupPanelPosition (sectionElem: HTMLElement):Point { + let pos: ClientRect = sectionElem.getBoundingClientRect(); + let offsetX: number = -30; + const offsetY: number = pos.height / 2; + return new Point((pos.right + offsetX), (pos.top - offsetY + window.pageYOffset)); + }; + + private setPopupPanelTitle(component: LeftPaletteComponent): void { + if (component.categoryType === LeftPaletteMetadataTypes.Group) { + this.panelTitle = "Add Group"; + return; + } + + if (component.categoryType === LeftPaletteMetadataTypes.Policy) { + this.panelTitle = "Add Policy"; + return; + } + } + + private showPopupPanel(displayComponent:LeftPaletteComponent, sectionElem: HTMLElement) { + if(!this.isShowPanel){ + this.displayComponent = displayComponent; + this.setPopupPanelTitle(displayComponent); + this.popupPanelPosition = this.getPopupPanelPosition(sectionElem); + this.isShowPanel = true; + } + }; + + private hidePopupPanel() { + if(this.isShowPanel){ + this.isShowPanel = false; + } + }; +} diff --git a/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html index 6d76f0ad06..fc6e821f34 100644 --- a/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html +++ b/catalog-ui/src/app/ng2/components/ui/popover/popover-content.component.html @@ -15,7 +15,8 @@ <ng-content></ng-content> <div class="popover-footer"> <button *ngFor="let buttonName of buttonsNames" - class="tlv-btn {{buttons[buttonName].cssClass}}" + class="tlv-btn {{buttons[buttonName].cssClass}}" + [attr.data-tests-id]="'filter-' + buttons[buttonName].text.toLowerCase() + '-button'" [disabled] = "buttons[buttonName].getDisabled && buttons[buttonName].getDisabled()" (click) = "buttons[buttonName].callback()">{{buttons[buttonName].text}}</button> </div> diff --git a/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts b/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts index 21d2bbad43..f4b410347b 100644 --- a/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts +++ b/catalog-ui/src/app/ng2/components/ui/tabs/tabs.component.ts @@ -23,6 +23,8 @@ import { Tab } from './tab/tab.component'; import { ViewEncapsulation } from '@angular/core'; import { trigger, state, style, transition, animate, keyframes } from '@angular/core'; +export {Tab}; + @Component({ selector: 'tabs', templateUrl: './tabs.component.html', diff --git a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts index 526096524c..44314734c3 100644 --- a/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts +++ b/catalog-ui/src/app/ng2/components/ui/ui-elements.module.ts @@ -27,6 +27,10 @@ import {ModalModule} from "./modal/modal.module"; import {PopoverModule} from "./popover/popover.module"; import {SearchBarComponent} from "./search-bar/search-bar.component"; import {SearchWithAutoCompleteComponent} from "./search-with-autocomplete/search-with-autocomplete.component"; +import {PalettePopupPanelComponent} from "./palette-popup-panel/palette-popup-panel.component"; +import {ZoneContainerComponent} from "./canvas-zone/zone-container.component"; +import {ZoneInstanceComponent } from "./canvas-zone/zone-instance/zone-instance.component"; +import {PaletteAnimationComponent} from "./palette-animation/palette-animation.component" import {TabModule} from "./tabs/tabs.module"; import {TooltipModule} from "./tooltip/tooltip.module"; import {CommonModule} from "@angular/common"; @@ -35,15 +39,21 @@ import {BrowserModule} from "@angular/platform-browser"; import {MultiStepsWizardModule} from "./multi-steps-wizard/multi-steps-wizard.module"; import {MenuListModule} from "./menu/menu-list.module"; import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-ng2.module"; +//import {SdcUiComponentsModule} from "sdc-ui/lib/angular"; @NgModule({ declarations: [ LoaderComponent, SearchBarComponent, - SearchWithAutoCompleteComponent - ], + SearchWithAutoCompleteComponent, + PalettePopupPanelComponent, + ZoneContainerComponent, + ZoneInstanceComponent, + PaletteAnimationComponent +], imports: [ + //SdcUiComponentsModule, BrowserModule, FormsModule, CommonModule, @@ -63,6 +73,9 @@ import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-n MultiStepsWizardModule, SearchBarComponent, SearchWithAutoCompleteComponent, + PalettePopupPanelComponent, + ZoneContainerComponent, + ZoneInstanceComponent, DynamicElementModule, NavbarModule, FormElementsModule, @@ -71,10 +84,10 @@ import {MenuListNg2Module} from "../downgrade-wrappers/menu-list-ng2/menu-list-n TabModule, TooltipModule, MenuListModule, - MenuListNg2Module + MenuListNg2Module, + PaletteAnimationComponent ], - - entryComponents: [SearchWithAutoCompleteComponent] + entryComponents: [SearchWithAutoCompleteComponent, PalettePopupPanelComponent, ZoneContainerComponent, ZoneInstanceComponent, PaletteAnimationComponent] }) export class UiElementsModule {} diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts index a097fb04ea..bf5ec4c4f6 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/connection-wizard.service.ts @@ -1,3 +1,4 @@ +import * as _ from "lodash"; import {ConnectRelationModel} from "../../../models/graph/connectRelationModel"; import {Injectable} from "@angular/core"; import { Requirement, Capability} from "app/models"; diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html index 0c9d9e6e26..a45f07b3e2 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.html @@ -3,6 +3,5 @@ [selectedReqOrCapModel]="connectWizardService.selectedMatch && (connectWizardService.selectedMatch.isFromTo ? connectWizardService.selectedMatch.requirement : connectWizardService.selectedMatch.capability)" [currentComponent]="connectWizardService.currentComponent" [componentInstanceId]="connectWizardService.connectRelationModel.fromNode.componentInstance.uniqueId" - (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)" - (updateCapabilityProperties)="onCapabilityPropertiesUpdate($event)"> + (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)"> </select-requirement-or-capability>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts index edbbf8a0a3..054d38b063 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/from-node-step/from-node-step.component.ts @@ -32,10 +32,6 @@ export class FromNodeStepComponent implements IStepComponent, OnInit{ return true; } - onCapabilityPropertiesUpdate(capabilityProperties:Array<PropertyModel>) { - this.connectWizardService.selectedMatch.capabilityProperties = capabilityProperties; - } - private updateSelectedReqOrCap = (selected:Requirement|Capability):void => { if(!selected){ this.connectWizardService.selectedMatch = null; diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html index 9e34893272..9b1df69d77 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.html @@ -5,7 +5,7 @@ </div> <div class="properties-table-container"> <properties-table class="properties-table" - (valueChanged)="propertyValueChanged($event)" + (propertyChanged)="propertyValueChanged($event)" [fePropertiesMap]="capabilityPropertiesMap" [selectedPropertyId]="''" [hidePropertyType]="true"> diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts index 3e48785a3c..946d1858dc 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/properties-step/properties-step.component.ts @@ -25,7 +25,7 @@ export class PropertiesStepComponent implements IStepComponent{ constructor(@Inject(forwardRef(() => ConnectionWizardService)) public connectWizardService: ConnectionWizardService, private componentInstanceServiceNg2:ComponentInstanceServiceNg2, private propertiesUtils:PropertiesUtils) { - this.capabilityPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({'capability' : connectWizardService.selectedMatch.capabilityProperties}, false); + this.capabilityPropertiesMap = this.propertiesUtils.convertPropertiesMapToFEAndCreateChildren({'capability' : connectWizardService.selectedMatch.capability.properties}, false); } ngOnInit() { @@ -42,7 +42,8 @@ export class PropertiesStepComponent implements IStepComponent{ propertyValueChanged = (property: PropertyFEModel) => { if (!property.isDeclared) { const propChangedIdx = this.connectWizardService.changedCapabilityProperties.indexOf(property); - if (this.componentInstanceServiceNg2.hasPropertyChanged(property)) { + if (property.hasValueObjChanged()) { + // if (this.componentInstanceServiceNg2.hasPropertyChanged(property)) { console.log("==>" + this.constructor.name + ": propertyValueChanged " + property); if (propChangedIdx === -1) { this.connectWizardService.changedCapabilityProperties.push(property); diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html index 67bb12e6fc..a8343ce7e2 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.html @@ -4,6 +4,5 @@ [selectedReqOrCapOption]="displayRequirementsOrCapabilities" [currentComponent]="connectWizardService.currentComponent" [componentInstanceId]="connectWizardService.connectRelationModel.toNode.componentInstance.uniqueId" - (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)" - (updateCapabilityProperties)="onCapabilityPropertiesUpdate($event)"> + (updateSelectedReqOrCap)="updateSelectedReqOrCap($event)"> </select-requirement-or-capability>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts index 9c7bf4dfe6..ea3b129c7b 100644 --- a/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts +++ b/catalog-ui/src/app/ng2/pages/connection-wizard/to-node-step/to-node-step.component.ts @@ -43,10 +43,6 @@ export class ToNodeStepComponent implements IStepComponent{ return false; } - onCapabilityPropertiesUpdate(capabilityProperties:Array<PropertyModel>) { - this.connectWizardService.selectedMatch.capabilityProperties = capabilityProperties; - } - private updateSelectedReqOrCap = (selected:Requirement|Capability):void => { if (!selected) { if (this.connectWizardService.selectedMatch.isFromTo) { diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts index 203c75dd11..907f7638bb 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.module.ts @@ -32,6 +32,7 @@ import {DataTypeService} from "../../services/data-type.service"; import {PropertiesAssignmentComponent} from "./properties-assignment.page.component"; import {HierarchyNavService} from "./services/hierarchy-nav.service"; import {PropertiesUtils} from "./services/properties.utils"; +import {InputsUtils} from "./services/inputs.utils"; import {ComponentModeService} from "../../services/component-services/component-mode.service"; @NgModule({ @@ -53,7 +54,7 @@ import {ComponentModeService} from "../../services/component-services/component- exports: [ PropertiesAssignmentComponent ], - providers: [PropertiesService, HierarchyNavService, PropertiesUtils, DataTypeService, ComponentModeService] + providers: [PropertiesService, HierarchyNavService, PropertiesUtils, InputsUtils, DataTypeService, ComponentModeService] }) export class PropertiesAssignmentModule { diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html index beea3fe73f..03e9f1fa7e 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.html @@ -1,43 +1,50 @@ <div class="properties-assignment-page"> <div class="main-content"> <div class="left-column"> - <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true"> - <tab tabTitle="Properties"> - <properties-table class="properties-table" - [fePropertiesMap]="instanceFePropertiesMap" - [feInstanceNamesMap]="componentInstanceNamesMap" - [searchTerm]="searchQuery" - [selectedPropertyId]="selectedFlatProperty.path" - [propertyNameSearchText]="searchPropertyName" - [readonly]="isReadonly" - [isLoading]="loadingProperties" - [hasDeclareOption]="true" - (valueChanged)="propertyValueChanged($event)" - (propertySelected)="propertySelected($event)" - (selectPropertyRow)="selectPropertyRow($event)" - (selectChildProperty)="selectChildProperty($event)" - (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)" - (selectInstanceRow)="selectInstanceRow($event)"> + <div class="main-tabs-section"> + <tabs #propertyInputTabs tabStyle="round-tabs" (tabChanged)="tabChanged($event)" [hideIndicationOnTabChange]="true"> + <tab tabTitle="Properties"> + <properties-table class="properties-table" + [fePropertiesMap]="instanceFePropertiesMap" + [feInstanceNamesMap]="componentInstanceNamesMap" + [searchTerm]="searchQuery" + [selectedPropertyId]="selectedFlatProperty.path" + [propertyNameSearchText]="searchPropertyName" + [readonly]="isReadonly" + [isLoading]="loadingProperties || savingChangedData" + [hasDeclareOption]="true" + (propertyChanged)="dataChanged($event)" + (propertySelected)="propertySelected($event)" + (selectPropertyRow)="selectPropertyRow($event)" + (selectChildProperty)="selectChildProperty($event)" + (updateCheckedPropertyCount)="updateCheckedPropertyCount($event)" + (selectInstanceRow)="selectInstanceRow($event)"> </properties-table> - </tab> - <tab tabTitle="Inputs"> - <inputs-table class="properties-table" - [readonly]="isReadonly" - [inputs]="inputs | searchFilter:'name':searchQuery" - [instanceNamesMap]="componentInstanceNamesMap" - [isLoading]="loadingInputs" - (deleteInput)="deleteInput($event)" - (inputValueChanged)="inputValueChanged($event)"></inputs-table> - </tab> - </tabs> + </tab> + <tab tabTitle="Inputs"> + <inputs-table class="properties-table" + [readonly]="isReadonly" + [inputs]="inputs | searchFilter:'name':searchQuery" + [instanceNamesMap]="componentInstanceNamesMap" + [isLoading]="loadingInputs" + (deleteInput)="deleteInput($event)" + (inputChanged)="dataChanged($event)"> + </inputs-table> + </tab> + </tabs> + <div class="main-tabs-section-buttons"> + <button class="tlv-btn outline blue" [disabled]="!hasChangedData || savingChangedData" (click)="reverseChangedData()" data-tests-id="properties-reverse-button">Discard</button> + <button class="tlv-btn blue" [disabled]="!hasChangedData || !isValidChangedData || savingChangedData" (click)="doSaveChangedData()" data-tests-id="properties-save-button">{{savingChangedData ? 'Saving' : 'Save'}}</button> + </div> + </div> <div class="header"> - <div class="search-filter-container" [class.without-filter]="isInpusTabSelected"> - <span *ngIf="displayClearSearch && !isInpusTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span> + <div class="search-filter-container" [class.without-filter]="isInputsTabSelected"> + <span *ngIf="displayClearSearch && isPropertiesTabSelected" (click)="clickOnClearSearch()" class="clear-filter" data-tests-id="clear-filter-button">Clear All</span> <input type="text" class="search-box" placeholder="Search" [(ngModel)]="searchQuery" data-tests-id="search-box"/> <span class="sprite search-icon" data-tests-id="search-button"></span> - <filter-properties-assignment *ngIf="!isInpusTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment> + <filter-properties-assignment *ngIf="isPropertiesTabSelected" #advanceSearch class="advance-search" [componentType]="component.componentType" (searchProperties)="searchPropertiesInstances($event)"></filter-properties-assignment> </div> - <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly" (click)="declareProperties()" data-tests-id="declare-button">Declare</button> + <button class="tlv-btn blue declare-button" [disabled]="!checkedPropertiesCount || isReadonly || hasChangedData" (click)="declareProperties()" data-tests-id="declare-button">Declare</button> </div> </div> <div class="right-column gray-border"> @@ -48,10 +55,10 @@ <div class="hierarchy-header white-sub-header"> <span tooltip="{{component.name}}">{{component.name}}</span> </div> - <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInpusTabSelected">No data to display</div> + <div *ngIf="!instancesNavigationData || instancesNavigationData.length === 0 || isInputsTabSelected">No data to display</div> <hierarchy-navigation class="hierarchy-nav" (updateSelected)="onInstanceSelectedUpdate($event)" - [displayData]="isInpusTabSelected ? []: instancesNavigationData" + [displayData]="isInputsTabSelected ? []: instancesNavigationData" [selectedItem]="selectedInstanceData.uniqueId" [displayOptions]="hierarchyInstancesDisplayOptions"></hierarchy-navigation> </div> @@ -59,12 +66,12 @@ <tab tabTitle="Property Structure"> <div class="hierarchy-nav-container"> <div class="hierarchy-header white-sub-header" [class.selected]="selectedFlatProperty.path == propertyStructureHeader"> - <span tooltip="{{!isInpusTabSelected ? propertyStructureHeader : ''}}">{{!isInpusTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span> + <span tooltip="{{isPropertiesTabSelected ? propertyStructureHeader : ''}}">{{isPropertiesTabSelected ? (propertyStructureHeader || "No Property Selected") : "No Property Selected"}}</span> </div> - <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInpusTabSelected">No data to display</div> + <div *ngIf="!propertiesNavigationData || propertiesNavigationData.length === 0 || isInputsTabSelected">No data to display</div> <hierarchy-navigation class="hierarchy-nav" (updateSelected)="onPropertySelectedUpdate($event)" - [displayData]="isInpusTabSelected ? [] : propertiesNavigationData" + [displayData]="isInputsTabSelected ? [] : propertiesNavigationData" [selectedItem]="selectedFlatProperty.path" [displayOptions]="hierarchyPropertiesDisplayOptions"></hierarchy-navigation> </div> @@ -72,4 +79,9 @@ </tabs> </div> </div> + <template #saveChangedDataModalContentTemplate> + <loader [display]="savingChangedData" [size]="'medium'" [relative]="true"></loader> + Your changes{{isValidChangedData ? '' : ' (invalid)'}} have not been saved.<br> + Do you want to {{isValidChangedData ? 'save' : 'discard'}} them? + </template> </div> diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less index 03974bf723..6de6dda7bb 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.less @@ -76,6 +76,7 @@ outline: none; font-style: italic; color:@ng2-med-dark-gray; + width: auto; &::-moz-placeholder { color:@ng2-med-dark-gray;} &::-webkit-input-placeholder{ color:@ng2-med-dark-gray;} @@ -113,6 +114,17 @@ top: 0; right: 0; } + + .main-tabs-section { + position: relative; + + .main-tabs-section-buttons { + position: absolute; + top: 45px; + right: 0; + padding: 4px; + } + } } .right-column { diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts index 9603648bd8..40818bce78 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/properties-assignment.page.component.ts @@ -18,11 +18,11 @@ * ============LICENSE_END========================================================= */ -import {Component, ViewChild, ElementRef, Renderer, Inject} from "@angular/core"; +import * as _ from "lodash"; +import {Component, ViewChild, Inject, TemplateRef} from "@angular/core"; import { PropertiesService } from "../../services/properties.service"; -import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData } from "app/models"; +import { PropertyFEModel, InstanceFePropertiesMap, InstanceBePropertiesMap, InstancePropertiesAPIMap, Component as ComponentData, FilterPropertiesAssignmentData, ModalModel, ButtonModel } from "app/models"; import { ResourceType } from "app/utils"; -import property = require("lodash/property"); import {ComponentServiceNg2} from "../../services/component-services/component.service"; import {ComponentInstanceServiceNg2} from "../../services/component-instance-services/component-instance.service" import { InputBEModel, InputFEModel, ComponentInstance, PropertyBEModel, DerivedFEProperty, ResourceInstance, SimpleFlatProperty } from "app/models"; @@ -35,6 +35,9 @@ import {PropertyRowSelectedEvent} from "../../components/logic/properties-table/ import {HierarchyNavService} from "./services/hierarchy-nav.service"; import {PropertiesUtils} from "./services/properties.utils"; import {ComponentModeService} from "../../services/component-services/component-mode.service"; +import {ModalService} from "../../services/modal.service"; +import {Tabs, Tab} from "../../components/ui/tabs/tabs.component"; +import {InputsUtils} from "./services/inputs.utils"; @Component({ templateUrl: './properties-assignment.page.component.html', @@ -64,24 +67,36 @@ export class PropertiesAssignmentComponent { hierarchyInstancesDisplayOptions:HierarchyDisplayOptions = new HierarchyDisplayOptions('uniqueId', 'name'); displayClearSearch = false; searchPropertyName:string; - isInpusTabSelected:boolean; + currentMainTab:Tab; + isInputsTabSelected:boolean; + isPropertiesTabSelected:boolean; isReadonly:boolean; loadingInstances:boolean = false; loadingInputs:boolean = false; loadingProperties:boolean = false; - - @ViewChild('hierarchyNavTabs') hierarchyNavTabs: ElementRef; - @ViewChild('propertyInputTabs') propertyInputTabs: ElementRef; + changedData:Array<PropertyFEModel|InputFEModel>; + hasChangedData:boolean; + isValidChangedData:boolean; + savingChangedData:boolean; + stateChangeStartUnregister:Function; + + @ViewChild('hierarchyNavTabs') hierarchyNavTabs: Tabs; + @ViewChild('propertyInputTabs') propertyInputTabs: Tabs; @ViewChild('advanceSearch') advanceSearch: FilterPropertiesAssignmentComponent; + @ViewChild('saveChangedDataModalContentTemplate') saveChangedDataModalContentTemplateRef: TemplateRef<void>; constructor(private propertiesService: PropertiesService, private hierarchyNavService: HierarchyNavService, private propertiesUtils:PropertiesUtils, + private inputsUtils:InputsUtils, private componentServiceNg2:ComponentServiceNg2, private componentInstanceServiceNg2:ComponentInstanceServiceNg2, @Inject("$stateParams") _stateParams, - private renderer: Renderer, + @Inject("$scope") private $scope:ng.IScope, + @Inject("$state") private $state:ng.ui.IStateService, + @Inject("Notification") private Notification:any, private componentModeService:ComponentModeService, + private ModalService:ModalService, private EventListenerService:EventListenerService) { this.instanceFePropertiesMap = new InstanceFePropertiesMap(); @@ -91,6 +106,10 @@ export class PropertiesAssignmentComponent { this.component = _stateParams.component; this.EventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.onCheckout); this.updateViewMode(); + + this.changedData = []; + this.updateHasChangedData(); + this.isValidChangedData = true; } ngOnInit() { @@ -102,7 +121,9 @@ export class PropertiesAssignmentComponent { .getComponentInputs(this.component) .subscribe(response => { _.forEach(response.inputs, (input: InputBEModel) => { - this.inputs.push(new InputFEModel(input)); //only push items that were declared via SDC + const newInput: InputFEModel = new InputFEModel(input); + this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue); + this.inputs.push(newInput); //only push items that were declared via SDC }); this.loadingInputs = false; @@ -123,10 +144,22 @@ export class PropertiesAssignmentComponent { this.selectFirstInstanceByDefault(); }, error => {}); //ignore error + this.stateChangeStartUnregister = this.$scope.$on('$stateChangeStart', (event, toState, toParams) => { + // stop if has changed properties + if (this.hasChangedData) { + event.preventDefault(); + this.openChangedDataModal().then((proceed) => { + if (proceed) { + this.$state.go(toState, toParams); + } + }); + } + }); }; ngOnDestroy() { this.EventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT); + this.stateChangeStartUnregister(); } selectFirstInstanceByDefault = () => { @@ -147,12 +180,23 @@ export class PropertiesAssignmentComponent { onInstanceSelectedUpdate = (resourceInstance: ResourceInstance) => { console.log("==>" + this.constructor.name + ": onInstanceSelectedUpdate"); + + // stop if has changed properties + if (this.hasChangedData) { + this.openChangedDataModal().then((proceed) => { + if (proceed) { + this.onInstanceSelectedUpdate(resourceInstance); + } + }); + return; + } + let instanceBePropertiesMap: InstanceBePropertiesMap = new InstanceBePropertiesMap(); this.selectedInstanceData = resourceInstance; this.selectedInstanceType = resourceInstance.originType; this.loadingProperties = true; - if(this.isInput(resourceInstance.originType)) { + if (this.isInput(resourceInstance.originType)) { this.componentInstanceServiceNg2 .getComponentInstanceInputs(this.component, resourceInstance) .subscribe(response => { @@ -160,7 +204,8 @@ export class PropertiesAssignmentComponent { this.processInstancePropertiesResponse(instanceBePropertiesMap, true); this.loadingProperties = false; - }, error => {}); //ignore error + }, error => { + }); //ignore error } else { this.componentInstanceServiceNg2 .getComponentInstanceProperties(this.component, resourceInstance.uniqueId) @@ -168,14 +213,15 @@ export class PropertiesAssignmentComponent { instanceBePropertiesMap[resourceInstance.uniqueId] = response; this.processInstancePropertiesResponse(instanceBePropertiesMap, false); this.loadingProperties = false; - }, error => {}); //ignore error + }, error => { + }); //ignore error } - if(resourceInstance.componentName === "vnfConfiguration") { + if (resourceInstance.componentName === "vnfConfiguration") { this.isReadonly = true; } - if( this.searchPropertyName ){ + if (this.searchPropertyName) { this.clearSearch(); } //clear selected property from the navigation @@ -193,41 +239,31 @@ export class PropertiesAssignmentComponent { /*** VALUE CHANGE EVENTS ***/ - propertyValueChanged = (event: PropertyFEModel) => { - console.log("==>" + this.constructor.name + ": propertyValueChanged " + event); - // Copying the actual value from the object ref into the value if it's from a complex type - event.value = event.getJSONValue(); - - if (this.isInput(this.selectedInstanceData.originType)) { - console.log("I want to update input value on the resource instance"); - let inputToUpdate = new PropertyBEModel(event); - this.componentInstanceServiceNg2 - .updateInstanceInput(this.component, this.selectedInstanceData.uniqueId, inputToUpdate) - .subscribe(response => { - console.log("Update resource instance input response: ", response); - }, error => {}); //ignore error - } - else { - let propertyBe = new PropertyBEModel(event); - this.componentInstanceServiceNg2 - .updateInstanceProperty(this.component, this.selectedInstanceData.uniqueId, propertyBe) - .subscribe(response => { - console.log("Update resource instance property response: ", response); - }, error => {}); //ignore error - console.log(event); + dataChanged = (item:PropertyFEModel|InputFEModel) => { + let itemHasChanged; + if (this.isPropertiesTabSelected && item instanceof PropertyFEModel) { + itemHasChanged = item.hasValueObjChanged(); + } else if (this.isInputsTabSelected && item instanceof InputFEModel) { + itemHasChanged = item.hasDefaultValueChanged(); } - }; - - inputValueChanged = (event) => { - console.log("==>" + this.constructor.name + ": inputValueChanged"); - let inputToUpdate = new PropertyBEModel(event); + const dataChangedIdx = this.changedData.findIndex((changedItem) => changedItem === item); + if (itemHasChanged) { + if (dataChangedIdx === -1) { + this.changedData.push(item); + } + } else { + if (dataChangedIdx !== -1) { + this.changedData.splice(dataChangedIdx, 1); + } + } - this.componentServiceNg2 - .updateComponentInput(this.component, inputToUpdate) - .subscribe(response => { - console.log("updated the component input and got this response: ", response); - }, error => {}); //ignore error + if (this.isPropertiesTabSelected) { + this.isValidChangedData = this.changedData.every((changedItem) => (<PropertyFEModel>changedItem).valueObjIsValid); + } else if (this.isInputsTabSelected) { + this.isValidChangedData = this.changedData.every((changedItem) => (<InputFEModel>changedItem).defaultValueObjIsValid); + } + this.updateHasChangedData(); }; @@ -272,7 +308,7 @@ export class PropertiesAssignmentComponent { // Set selected property in table this.selectedFlatProperty = this.hierarchyNavService.createSimpleFlatProperty(property, instanceName); - this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Property Structure']); + this.hierarchyNavTabs.triggerTabChange('Property Structure'); }; @@ -280,13 +316,27 @@ export class PropertiesAssignmentComponent { this.selectedInstanceData = _.find(this.instancesNavigationData, (instance:ComponentInstance) => { return instance.name == $event; }); - this.renderer.invokeElementMethod( - this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); + this.hierarchyNavTabs.triggerTabChange('Composition'); }; tabChanged = (event) => { + // stop if has changed properties + if (this.hasChangedData) { + this.openChangedDataModal().then((proceed) => { + if (proceed) { + this.propertyInputTabs.selectTab(this.propertyInputTabs.tabs.find((tab) => tab.title === event.title)); + } + }); + + // return to show the current tab + this.propertyInputTabs.triggerTabChange(this.currentMainTab.title); + return; + } + console.log("==>" + this.constructor.name + ": tabChanged " + event); - this.isInpusTabSelected = event.title === "Inputs"; + this.currentMainTab = this.propertyInputTabs.tabs.find((tab) => tab.title === event.title); + this.isPropertiesTabSelected = this.currentMainTab.title === "Properties"; + this.isInputsTabSelected = this.currentMainTab.title === "Inputs"; this.propertyStructureHeader = null; this.searchQuery = ''; }; @@ -320,12 +370,180 @@ export class PropertiesAssignmentComponent { this.checkedPropertiesCount = 0; _.forEach(response, (input: InputBEModel) => { let newInput: InputFEModel = new InputFEModel(input); + this.inputsUtils.resetInputDefaultValue(newInput, input.defaultValue); this.inputs.push(newInput); this.updatePropertyValueAfterDeclare(newInput); }); }, error => {}); //ignore error }; + saveChangedData = ():Promise<(PropertyBEModel|InputBEModel)[]> => { + return new Promise((resolve, reject) => { + if (!this.isValidChangedData) { + reject('Changed data is invalid - cannot save!'); + return; + } + if (!this.changedData.length) { + resolve([]); + return; + } + + // make request and its handlers + let request; + let handleSuccess, handleError; + if (this.isPropertiesTabSelected) { + const changedProperties: PropertyBEModel[] = this.changedData.map((changedProp) => { + changedProp = <PropertyFEModel>changedProp; + const propBE = new PropertyBEModel(changedProp); + propBE.value = changedProp.getJSONValue(); + return propBE; + }); + + if (this.isInput(this.selectedInstanceData.originType)) { + request = this.componentInstanceServiceNg2 + .updateInstanceInputs(this.component, this.selectedInstanceData.uniqueId, changedProperties); + handleSuccess = (response) => { + // reset each changed property with new value and remove it from changed properties list + response.forEach((resInput) => { + const changedProp = <PropertyFEModel>this.changedData.shift(); + this.propertiesUtils.resetPropertyValue(changedProp, resInput.value); + }); + console.log('updated instance inputs:', response); + }; + } else { + request = this.componentInstanceServiceNg2 + .updateInstanceProperties(this.component, this.selectedInstanceData.uniqueId, changedProperties) + handleSuccess = (response) => { + // reset each changed property with new value and remove it from changed properties list + response.forEach((resProp) => { + const changedProp = <PropertyFEModel>this.changedData.shift(); + this.propertiesUtils.resetPropertyValue(changedProp, resProp.value); + }); + resolve(response); + console.log("updated instance properties: ", response); + }; + } + } else if (this.isInputsTabSelected) { + const changedInputs: InputBEModel[] = this.changedData.map((changedInput) => { + changedInput = <InputFEModel>changedInput; + const inputBE = new InputBEModel(changedInput); + inputBE.defaultValue = changedInput.getJSONDefaultValue(); + return inputBE; + }); + request = this.componentServiceNg2 + .updateComponentInputs(this.component, changedInputs); + handleSuccess = (response) => { + // reset each changed property with new value and remove it from changed properties list + response.forEach((resInput) => { + const changedInput = <InputFEModel>this.changedData.shift(); + this.inputsUtils.resetInputDefaultValue(changedInput, resInput.defaultValue); + }); + console.log("updated the component inputs and got this response: ", response); + } + } + + this.savingChangedData = true; + request.subscribe( + (response) => { + this.savingChangedData = false; + handleSuccess && handleSuccess(response); + this.updateHasChangedData(); + resolve(response); + }, + (error) => { + this.savingChangedData = false; + handleError && handleError(error); + this.updateHasChangedData(); + reject(error); + } + ); + }); + }; + + reverseChangedData = ():void => { + // make reverse item handler + let handleReverseItem; + if (this.isPropertiesTabSelected) { + handleReverseItem = (changedItem) => { + changedItem = <PropertyFEModel>changedItem; + this.propertiesUtils.resetPropertyValue(changedItem, changedItem.value); + }; + } else if (this.isInputsTabSelected) { + handleReverseItem = (changedItem) => { + changedItem = <InputFEModel>changedItem; + this.inputsUtils.resetInputDefaultValue(changedItem, changedItem.defaultValue); + }; + } + + this.changedData.forEach(handleReverseItem); + this.changedData = []; + this.updateHasChangedData(); + }; + + updateHasChangedData = ():boolean => { + const curHasChangedData:boolean = (this.changedData.length > 0); + if (curHasChangedData !== this.hasChangedData) { + this.hasChangedData = curHasChangedData; + this.$scope.$emit('setWorkspaceTopBarActive', !this.hasChangedData); + } + return this.hasChangedData; + }; + + doSaveChangedData = ():void => { + this.saveChangedData().then( + () => { + this.Notification.success({ + message: 'Successfully saved changes', + title: 'Saved' + }); + }, + () => { + this.Notification.error({ + message: 'Failed to save changes!', + title: 'Failure' + }); + } + ); + }; + + openChangedDataModal = ():Promise<boolean> => { + let modalTitle; + if (this.isPropertiesTabSelected) { + modalTitle = `Unsaved properties for ${this.selectedInstanceData.name}`; + } else if (this.isInputsTabSelected) { + modalTitle = `Unsaved inputs for ${this.component.name}`; + } + + return new Promise<boolean>((resolve) => { + const modal = this.ModalService.createCustomModal(new ModalModel( + 'sm', + modalTitle, + null, + [ + new ButtonModel('Cancel', 'outline grey', () => { + modal.instance.close(); + resolve(false); + }), + new ButtonModel('Discard', 'outline blue', () => { + this.reverseChangedData(); + modal.instance.close(); + resolve(true); + }), + new ButtonModel('Save', 'blue', () => { + this.saveChangedData().then(() => { + modal.instance.close(); + resolve(true); + }, () => { + modal.instance.close(); + resolve(false); + }); + }, () => !this.isValidChangedData) + ] + )); + this.ModalService.addDynamicTemplateToModal(modal, this.saveChangedDataModalContentTemplateRef); + modal.instance.open(); + }); + }; updatePropertyValueAfterDeclare = (input: InputFEModel) => { if (this.instanceFePropertiesMap[input.instanceUniqueId]) { @@ -346,12 +564,12 @@ export class PropertiesAssignmentComponent { }; setInputTabIndication = (numInputs: number): void => { - this.renderer.invokeElementMethod(this.propertyInputTabs, 'setTabIndication', ['Inputs', numInputs]); + this.propertyInputTabs.setTabIndication('Inputs', numInputs); }; deleteInput = (input: InputFEModel) => { console.log("==>" + this.constructor.name + ": deleteInput"); - let inputToDelete = new PropertyBEModel(input); + let inputToDelete = new InputBEModel(input); this.componentServiceNg2 .deleteInput(this.component, inputToDelete) @@ -389,7 +607,7 @@ export class PropertiesAssignmentComponent { this.processInstancePropertiesResponse(response, false); this.hierarchyPropertiesDisplayOptions.searchText = filterData.propertyName;//mark results in tree this.searchPropertyName = filterData.propertyName;//mark in table - this.renderer.invokeElementMethod(this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); + this.hierarchyNavTabs.triggerTabChange('Composition'); this.propertiesNavigationData = []; this.displayClearSearch = true; }, error => {}); //ignore error @@ -408,12 +626,11 @@ export class PropertiesAssignmentComponent { clickOnClearSearch = () => { this.clearSearch(); this.selectFirstInstanceByDefault(); - this.renderer.invokeElementMethod( - this.hierarchyNavTabs, 'triggerTabChange', ['Composition']); + this.hierarchyNavTabs.triggerTabChange('Composition'); }; private isInput = (instanceType:string):boolean =>{ - return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC; + return instanceType === ResourceType.VF || instanceType === ResourceType.PNF || instanceType === ResourceType.CVFC || instanceType === ResourceType.CR; } } diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts index 016b04788e..1a800baac7 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/hierarchy-nav.service.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { SimpleFlatProperty, PropertyFEModel, DerivedFEProperty } from 'app/models'; diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts new file mode 100644 index 0000000000..408a00e7b4 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/inputs.utils.ts @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import { Injectable } from '@angular/core'; +import { InputFEModel} from "app/models"; + +@Injectable() +export class InputsUtils { + + constructor() {} + + public initDefaultValueObject = (input: InputFEModel): void => { + input.resetDefaultValueObjValidation(); + input.defaultValueObj = input.getDefaultValueObj(); + input.updateDefaultValueObjOrig(); + }; + + public resetInputDefaultValue = (input: InputFEModel, newDefaultValue: string): void => { + input.defaultValue = newDefaultValue; + this.initDefaultValueObject(input); + } + +} diff --git a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts index 8f46c6f603..e7b59b96ba 100644 --- a/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts +++ b/catalog-ui/src/app/ng2/pages/properties-assignment/services/properties.utils.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { DataTypeModel, PropertyFEModel, PropertyBEModel, InstanceBePropertiesMap, InstanceFePropertiesMap, DerivedFEProperty, DerivedPropertyType, InputFEModel} from "app/models"; import { DataTypeService } from "app/ng2/services/data-type.service"; @@ -47,10 +48,13 @@ export class PropertiesUtils { let newFEProp: PropertyFEModel = new PropertyFEModel(property); //Convert property to FE - if (newFEProp.derivedDataType == DerivedPropertyType.COMPLEX) { //Create children if prop is not simple, list, or map. - newFEProp.flattenedChildren = this.createFlattenedChildren(newFEProp.type, newFEProp.name); - } - if (newFEProp.getInputValues && newFEProp.getInputValues.length) { //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children + this.initValueObjectRef(newFEProp); //initialize valueObj. + propertyFeArray.push(newFEProp); + newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children + this.dataTypeService.checkForCustomBehavior(newFEProp); + + //if this prop (or any children) are declared, set isDeclared and disable checkbox on parents/children + if (newFEProp.getInputValues && newFEProp.getInputValues.length) { newFEProp.getInputValues.forEach(propInputDetail => { let inputPath = propInputDetail.inputPath; if (!inputPath) { //TODO: this is a workaround until Marina adds inputPath @@ -63,10 +67,6 @@ export class PropertiesUtils { this.propertiesService.disableRelatedProperties(newFEProp, inputPath); }); } - this.initValueObjectRef(newFEProp); //initialize valueObj. - propertyFeArray.push(newFEProp); - newFEProp.updateExpandedChildPropertyId(newFEProp.name); //display only the first level of children - this.dataTypeService.checkForCustomBehavior(newFEProp); } }); instanceFePropertiesMap[instanceId] = propertyFeArray; @@ -103,33 +103,29 @@ export class PropertiesUtils { * Note: This logic is different than assignflattenedchildrenvalues - here we merge values, there we pick either the parents value, props value, or default value - without merging. */ public initValueObjectRef = (property: PropertyFEModel): void => { - if (property.derivedDataType == DerivedPropertyType.SIMPLE || property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component - property.valueObj = property.value || property.defaultValue; - if (property.isDeclared) { - if(typeof property.valueObj == 'object'){ - property.valueObj = JSON.stringify(property.valueObj); - } - }else if(property.valueObj && - property.type !== PROPERTY_TYPES.STRING && - property.type !== PROPERTY_TYPES.JSON && - PROPERTY_DATA.SCALAR_TYPES.indexOf(property.type) == -1){ - property.valueObj = JSON.parse(property.valueObj);//The valueObj contains the real value ans not the value as string + property.resetValueObjValidation(); + if (property.isDeclared) { //if property is declared, it gets a simple input instead. List and map values and pseudo-children will be handled in property component + property.valueObj = property.value || property.defaultValue || null; // use null for empty value object + if (property.valueObj && typeof property.valueObj == 'object') { + property.valueObj = JSON.stringify(property.valueObj); } } else { - if (property.derivedDataType == DerivedPropertyType.LIST) { - property.valueObj = _.merge([], JSON.parse(property.defaultValue || '[]'), JSON.parse(property.value || '[]')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined. - } else { - property.valueObj = _.merge({}, JSON.parse(property.defaultValue || '{}'), JSON.parse(property.value || '{}')); //value object should be merged value and default value. Value takes higher precendence. Set valueObj to empty obj if undefined. - } - if ((property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) && Object.keys(property.valueObj).length) { + property.valueObj = property.getValueObj(); + if (property.derivedDataType == DerivedPropertyType.LIST || property.derivedDataType == DerivedPropertyType.MAP) { + property.flattenedChildren = []; Object.keys(property.valueObj).forEach((key) => { property.flattenedChildren.push(...this.createListOrMapChildren(property, key, property.valueObj[key])) }); - } else { + } else if (property.derivedDataType === DerivedPropertyType.COMPLEX) { + property.flattenedChildren = this.createFlattenedChildren(property.type, property.name); this.assignFlattenedChildrenValues(property.valueObj, property.flattenedChildren, property.name); + property.flattenedChildren.forEach((childProp) => { + property.childPropUpdated(childProp); + }); } } - } + property.updateValueObjOrig(); + }; /* * Loops through flattened properties array and to assign values @@ -142,22 +138,24 @@ export class PropertiesUtils { derivedPropArray.forEach((prop, index) => { let propNameInObj = prop.propertiesName.substring(prop.propertiesName.indexOf(parentName) + parentName.length + 1).split('#').join('.'); //extract everything after parent name - prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue + prop.valueObj = _.get(parentValueJSON, propNameInObj, prop.value || prop.defaultValue || null); //assign value -first value of parent if exists. If not, prop.value if not, prop.defaultvalue + prop.value = (prop.valueObj !== null && (typeof prop.valueObj) != 'string') ? JSON.stringify(prop.valueObj) : prop.valueObj; - if ( prop.isDeclared && typeof prop.valueObj == 'object') { //Stringify objects of items that are declared - prop.valueObj = JSON.stringify(prop.valueObj); - } else if(typeof prop.valueObj == PROPERTY_TYPES.STRING - && (prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN)){ //parse ints and non-string simple types - prop.valueObj = JSON.parse(prop.valueObj); + if ((prop.isDeclared || prop.type == PROPERTY_TYPES.STRING || prop.type == PROPERTY_TYPES.JSON)) { //Stringify objects of items that are declared or from type string/json + prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == 'object') ? JSON.stringify(prop.valueObj) : prop.valueObj; + } else if(prop.type == PROPERTY_TYPES.INTEGER || prop.type == PROPERTY_TYPES.FLOAT || prop.type == PROPERTY_TYPES.BOOLEAN){ //parse ints and non-string simple types + prop.valueObj = (prop.valueObj !== null && typeof prop.valueObj == PROPERTY_TYPES.STRING) ? JSON.parse(prop.valueObj) : prop.valueObj; } else { //parse strings that should be objects - if (prop.derivedDataType == DerivedPropertyType.COMPLEX && typeof prop.valueObj != 'object') { - prop.valueObj = JSON.parse(prop.valueObj || '{}'); - } else if (prop.derivedDataType == DerivedPropertyType.LIST && typeof prop.valueObj != 'object') { - prop.valueObj = JSON.parse(prop.valueObj || '[]'); - } else if (prop.derivedDataType == DerivedPropertyType.MAP && typeof prop.valueObj != 'object' && (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType)) { //dont parse values for children of map of simple - prop.valueObj = JSON.parse(prop.valueObj || '{}'); + if (prop.derivedDataType == DerivedPropertyType.COMPLEX) { + prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj; + } else if (prop.derivedDataType == DerivedPropertyType.LIST) { + prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '[]') : prop.valueObj; + } else if (prop.derivedDataType == DerivedPropertyType.MAP) { + if (!prop.isChildOfListOrMap || !prop.schema.property.isSimpleType) { + prop.valueObj = (prop.valueObj === null || typeof prop.valueObj != 'object') ? JSON.parse(prop.valueObj || '{}') : prop.valueObj; + } } - if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && Object.keys(prop.valueObj).length) { + if ((prop.derivedDataType == DerivedPropertyType.LIST || prop.derivedDataType == DerivedPropertyType.MAP) && typeof prop.valueObj == 'object' && prop.valueObj !== null && Object.keys(prop.valueObj).length) { let newProps: Array<DerivedFEProperty> = []; Object.keys(prop.valueObj).forEach((key) => { newProps.push(...this.createListOrMapChildren(prop, key, prop.valueObj[key]));//create new children, assign their values, and then add to array @@ -165,6 +163,8 @@ export class PropertiesUtils { propsToPushMap[index + 1] = newProps; } } + + prop.valueObj = PropertyFEModel.cleanValueObj(prop.valueObj); }); //add props after we're done looping (otherwise our loop gets messed up). Push in reverse order, so we dont mess up indexes. @@ -178,11 +178,10 @@ export class PropertiesUtils { if (nestedPath) { let newProp = property.flattenedChildren.find(prop => prop.propertiesName == nestedPath); newProp && this.assignFlattenedChildrenValues(JSON.parse(newValue), [newProp], property.name); + property.updateValueObjOrig(); } else { this.initValueObjectRef(property); } } - - } diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html new file mode 100644 index 0000000000..bbbf6ae694 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.html @@ -0,0 +1,5 @@ +<ui-element-dropdown data-tests-id="linkSrc" [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" class="cell link-selector" [values]="source" [(value)]="link.fromNode" (valueChange)="onSourceSelected($event)"></ui-element-dropdown> +<ui-element-dropdown data-tests-id="linkSrcCP" [readonly]="!link.isFirst || (link.isFirst && !link.canEdit)" class="cell link-selector" [values]="srcCP" [(value)]="link.fromCP" (valueChange)="onSrcCPSelected($event)"></ui-element-dropdown> +<ui-element-dropdown data-tests-id="linkTarget" [readonly]="!link.canEdit" class="cell link-selector" [values]="target" [(value)]="link.toNode" (valueChange)="onTargetSelected($event)"></ui-element-dropdown> +<ui-element-dropdown data-tests-id="linkTargetCP" [readonly]="!link.canEdit" class="cell link-selector" [values]="targetCP" [(value)]="link.toCP" (valueChange)="onTargetCPSelected($event)"></ui-element-dropdown> +<div class="cell remove" data-tests-id="removeLnk"><span *ngIf="link.canRemove" class="sprite-new delete-item-icon" (click)="removeRow()"></span></div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less new file mode 100644 index 0000000000..beec9bd567 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.less @@ -0,0 +1,21 @@ +@import './../../../../../assets/styles/variables.less'; +.remove { + display: flex; + align-items: center; + justify-content: center; +} + +.cell { + padding: 0; +} + +/deep/ .link-selector { + select { + height: 30px; + border: none; + stroke: none; + } + +} + + diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts new file mode 100644 index 0000000000..16433242d6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link-row.component.ts @@ -0,0 +1,110 @@ +import {Component, Input} from '@angular/core'; +import {DropdownValue} from "app/ng2/components/ui/form-components/dropdown/ui-element-dropdown.component"; +import {Link} from './link.model'; +import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; + +@Component({ + selector: 'link-row', + templateUrl: './link-row.component.html', + styleUrls: ['./link-row.component.less'] +}) + + +export class LinkRowComponent { + @Input() data:Array<ServicePathMapItem>; + @Input() link:Link; + @Input() removeRow:Function; + source:Array<DropdownValue> = []; + target: Array<DropdownValue> = []; + srcCP: Array<DropdownValue> = []; + targetCP: Array<DropdownValue> = []; + + ngOnChanges() { + if (this.data) { + this.parseInitialData(this.data); + } + } + + parseInitialData(data: Array<ServicePathMapItem>) { + this.source = this.convertValuesToDropDownOptions(data); + if (this.link.fromNode) { + let srcCPOptions = this.findOptions(data, this.link.fromNode); + if (!srcCPOptions) { return; } + this.srcCP = this.convertValuesToDropDownOptions(srcCPOptions); + if (this.link.fromCP) { + let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP); + if (!targetOptions) { return; } + this.target = this.convertValuesToDropDownOptions(targetOptions); + if (this.link.toNode) { + let targetCPOptions = this.findOptions(targetOptions, this.link.toNode); + if (!targetCPOptions) { return; } + this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); + } + } + } + } + + private findOptions(items: Array<ServicePathMapItem>, nodeOrCPId: string) { + let item = items.find((dataItem)=> nodeOrCPId === dataItem.id); + if (item && item.data && item.data.options) { + return item.data.options; + } + console.warn('no option was found to match selection of Node/CP with id:' + nodeOrCPId); + return null; + } + + private convertValuesToDropDownOptions(values: Array<ServicePathMapItem>) : Array<DropdownValue> { + let result = []; + for (let i = 0; i < values.length ; i++) { + result[result.length] = new DropdownValue(values[i].id, values[i].data.name); + } + return result; + } + + onSourceSelected(id) { + if (id) { + let srcCPOptions = this.findOptions(this.data, id); + this.srcCP = this.convertValuesToDropDownOptions(srcCPOptions); + this.link.fromCP = ''; + this.link.toNode = ''; + this.link.toCP = ''; + this.target = []; + this.targetCP = []; + } + } + + onSrcCPSelected (id) { + if (id) { + let srcCPData = this.data.find((dataItem)=> this.link.fromNode === dataItem.id).data; + let srcCPOptions = srcCPData.options; + let targetOptions = this.findOptions(srcCPOptions, id); + this.target = this.convertValuesToDropDownOptions(targetOptions); + this.link.fromCPOriginId = srcCPData.ownerId; + this.link.toNode = ''; + this.link.toCP = ''; + this.targetCP = []; + } + + } + + onTargetSelected(id) { + if (id) { + let srcCPOptions = this.findOptions(this.data, this.link.fromNode); + let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP); + let targetCPOptions = this.findOptions(targetOptions, id); + this.targetCP = this.convertValuesToDropDownOptions(targetCPOptions); + this.link.toCP = ''; + } + + } + + onTargetCPSelected(id) { + if (id) { + let srcCPOptions = this.findOptions(this.data, this.link.fromNode); + let targetOptions = this.findOptions(srcCPOptions, this.link.fromCP); + let targetCPOptions = this.findOptions(targetOptions, this.link.toNode); + let targetCPDataObj = targetCPOptions.find((dataItem)=> id === dataItem.id).data; + this.link.toCPOriginId = targetCPDataObj.ownerId; + } + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts new file mode 100644 index 0000000000..80128eb42e --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/link-row/link.model.ts @@ -0,0 +1,36 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +'use strict'; +import {ForwardingPathLink} from "app/models/forwarding-path-link"; + +export class Link extends ForwardingPathLink { + public canEdit:boolean = false; + public canRemove:boolean = false; + public isFirst:boolean = false; + + constructor(link: ForwardingPathLink, canEdit: boolean, canRemove: boolean, isFirst: boolean) { + super(link.fromNode,link.fromCP, link.toNode, link.toCP, link.fromCPOriginId, link.toCPOriginId); + this.canEdit = canEdit; + this.canRemove = canRemove; + this.isFirst = isFirst; + } +} + + diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html new file mode 100644 index 0000000000..96cd83eef6 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.html @@ -0,0 +1,43 @@ +<div class="service-path-creator"> + <form class="w-sdc-form"> + <div class="i-sdc-form-item" > + <label class="i-sdc-form-label required">Path Name</label> + <!-- <ui-element-input type="text" name="pathName" [value]="pathName" ></ui-element-input> --> + <input type="text" data-tests-id="pathName" name="pathName" [(ngModel)]="forwardingPath.name" [attr.maxLength]="100" /> <!-- TODO - make unique --> + </div> + + <div class="side-by-side"> + <div class="i-sdc-form-item" > + <label class="i-sdc-form-label">Protocol</label> + <!-- <ui-element-input type="text" name="protocol" [value]="protocol" ></ui-element-input> --> + <input type="text" data-tests-id="pathProtocol" name="protocol" [(ngModel)]="forwardingPath.protocol" [attr.maxLength]="100" /> + </div> + <div class="i-sdc-form-item" > + <label class="i-sdc-form-label">Destination Port Numbers</label> + <!-- <ui-element-input type="text" name="portNumbers" [value]="portNumbers" ></ui-element-input> --> + <input type="text" data-tests-id="pathPortNumbers" name="portNumbers" [(ngModel)]="forwardingPath.destinationPortNumber" pattern="[0-9,]*" /> <!-- TODO - validate delimiter --> + </div> + </div> + + <div class="separator-buttons"> + <span class="based-on-title">Based On</span> + <a (click)="addRow()" [ngClass]="{'disabled':!isExtendAllowed()}" data-tests-id="extendPathlnk">Extend Path</a> + </div> + + <div class="generic-table"> + <div class="header-row"> + <div class="cell header-cell" *ngFor="let header of headers"> + {{header}} + </div> + </div> + <div *ngIf="links && links.length === 0" class="no-row-text" > + There is no data to display + </div> + <div> + <link-row *ngFor="let link of links" [data]="linksMap" [link]="link" [removeRow]="removeRow" class="data-row" ></link-row> + </div> + </div> + + + </form> +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less new file mode 100644 index 0000000000..5c9e53e229 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.less @@ -0,0 +1,45 @@ +@import './../../../../assets/styles/variables.less'; +.service-path-creator { + font-family: @font-opensans-regular; + .separator-buttons { + margin: 10px 0; + display: flex; + justify-content: space-between; + } + .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 { + margin-right: 10px; + } + } + } + + .generic-table { + max-height: 233px; + .header-row .header-cell { + &:last-child { + padding: 0; + } + } + /deep/ .cell { + &:last-child { + min-width: 30px; + } + } + } + + .based-on-title { + text-transform: uppercase; + font-size: 18px; + font-family: @font-opensans-regular; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts new file mode 100644 index 0000000000..dac41a37bc --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.component.ts @@ -0,0 +1,137 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import * as _ from "lodash"; +import { Component, ElementRef, forwardRef, Inject } from '@angular/core'; +import {Link} from './link-row/link.model'; +import {ForwardingPath} from 'app/models/forwarding-path'; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {ForwardingPathLink} from "app/models/forwarding-path-link"; +import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; + +@Component({ + selector: 'service-path-creator', + templateUrl: './service-path-creator.component.html', + styleUrls:['./service-path-creator.component.less'], + providers: [ServiceServiceNg2] +}) + +export class ServicePathCreatorComponent { + + linksMap:Array<ServicePathMapItem>; + links:Array<Link> = []; + input:any; + headers: Array<string> = []; + removeRow: Function; + forwardingPath:ForwardingPath; + //isExtendAllowed:boolean = false; + + constructor(private serviceService: ServiceServiceNg2) { + this.forwardingPath = new ForwardingPath(); + this.links = [new Link(new ForwardingPathLink('', '', '', '', '', ''), true, false, true)]; + this.headers = ['Source', 'Source Connection Point', 'Target', 'Target Connection Point', ' ']; + this.removeRow = () => { + if (this.links.length === 1) { + return; + } + this.links.splice(this.links.length-1, 1); + this.enableCurrentRow(); + }; + } + + ngOnInit() { + this.serviceService.getNodesAndLinksMap(this.input.service).subscribe((res:any) => { + this.linksMap = res; + }); + this.processExistingPath(); + + } + + private processExistingPath() { + if (this.input.pathId) { + let forwardingPath = <ForwardingPath>{...this.input.service.forwardingPaths[this.input.pathId]}; + this.forwardingPath.name = forwardingPath.name; + this.forwardingPath.destinationPortNumber = forwardingPath.destinationPortNumber; + this.forwardingPath.protocol = forwardingPath.protocol; + this.forwardingPath.uniqueId = forwardingPath.uniqueId; + this.links = []; + _.forEach(forwardingPath.pathElements.listToscaDataDefinition, (link:ForwardingPathLink) => { + this.links[this.links.length] = new Link( link, false, false, false); + }); + this.links[this.links.length -1].canEdit = true; + this.links[this.links.length -1].canRemove = true; + this.links[0].isFirst = true; + + } + } + + isExtendAllowed():boolean { + if (this.links[this.links.length-1].toCP) { + return true; + } + return false; + } + + enableCurrentRow() { + this.links[this.links.length-1].canEdit = true; + if (this.links.length !== 1) { + this.links[this.links.length-1].canRemove = true; + } + } + + addRow() { + this.disableRows(); + this.links[this.links.length] = new Link( new ForwardingPathLink(this.links[this.links.length-1].toNode,this.links[this.links.length-1].toCP,'','',this.links[this.links.length-1].toCPOriginId,''),true, true, false); + } + + disableRows() { + for (let i = 0 ; i < this.links.length ; i++) { + this.links[i].canEdit = false; + this.links[i].canRemove = false; + } + } + + createPathLinksObject() { + for (let i = 0 ; i < this.links.length ; i++) { + let link = this.links[i]; + this.forwardingPath.addPathLink(link.fromNode, link.fromCP, link.toNode, link.toCP, link.fromCPOriginId, link.toCPOriginId); + } + } + + createServicePathData() { + this.createPathLinksObject(); + return this.forwardingPath; + } + + checkFormValidForSubmit():boolean { + if (this.forwardingPath.name && this.isPathValid() ) { + return true; + } + return false; + } + + isPathValid():boolean { + let lastLink = this.links[this.links.length -1] ; + if (lastLink.toNode && lastLink.toCP && lastLink.fromNode && lastLink.fromCP) { + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts new file mode 100644 index 0000000000..78005317a2 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-path-creator/service-path-creator.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {ServicePathCreatorComponent} from "./service-path-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 {LinkRowComponent} from './link-row/link-row.component' +@NgModule({ + declarations: [ + ServicePathCreatorComponent, + LinkRowComponent + ], + imports: [CommonModule, + FormsModule, + FormElementsModule, + UiElementsModule + ], + exports: [], + entryComponents: [ + ServicePathCreatorComponent + ], + providers: [] +}) +export class ServicePathCreatorModule { +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html new file mode 100644 index 0000000000..8a31c76998 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.html @@ -0,0 +1,21 @@ +<div class="service-path-list"> + <div class="add-path-link"><a (click)="onAddServicePath()" data-tests-id="add-service-path-lnk" >+ Add Path</a></div> + <div class="generic-table table-container" > + <div class="header-row"> + <div class="cell header-cell" *ngFor="let header of headers"> + {{header}} + </div> + </div> + <div *ngFor="let path of paths" class="data-row" > + <div class="cell" data-tests-id="path-name" >{{path.name}}</div> + <div class="cell path-action-buttons"> + <span class="sprite-new update-component-icon" (click)="onEditServicePath(path.uniqueId)" data-tests-id="update-service-path-btn" ></span> + <span class="sprite-new delete-item-icon" (click)="deletePath(path.uniqueId)" data-tests-id="delete-service-path-btn"></span> + </div> + </div> + <div *ngIf="paths && paths.length === 0" class="no-row-text" > + No paths have been added yet. + </div> + </div> + +</div>
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less new file mode 100644 index 0000000000..aff597fd85 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.less @@ -0,0 +1,21 @@ +@import './../../../../assets/styles/variables.less'; + +.add-path-link { + display: flex; + align-items: flex-end; + flex-direction: column; + padding-bottom: 10px; +} + +.generic-table { + max-height: 233px; +} + +.path-action-buttons { + display: flex; + align-items: center; + justify-content: space-between; + .sprite-new { + cursor: pointer; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts new file mode 100644 index 0000000000..04083e8685 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.component.ts @@ -0,0 +1,66 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import * as _ from "lodash"; +import {Component, ComponentRef} from '@angular/core'; +import {ForwardingPath} from "app/models/forwarding-path"; +import {ServiceServiceNg2} from "app/ng2/services/component-services/service.service"; +import {ModalService} from "app/ng2/services/modal.service"; +import {ModalComponent} from "app/ng2/components/ui/modal/modal.component"; + +@Component({ + selector: 'service-paths-list', + templateUrl: './service-paths-list.component.html', + styleUrls:['service-paths-list.component.less'], + providers: [ServiceServiceNg2, ModalService] +}) +export default class ServicePathsListComponent { + modalInstance: ComponentRef<ModalComponent>; + headers: Array<string> = []; + paths: Array<ForwardingPath> = []; + input:any; + onAddServicePath: Function; + onEditServicePath: Function; + + constructor(private serviceService:ServiceServiceNg2) { + this.headers = ['Path Name','Actions']; + } + + ngOnInit() { + _.forEach(this.input.service.forwardingPaths, (path: ForwardingPath)=> { + this.paths[this.paths.length] = path; + }); + this.paths.sort((a:ForwardingPath, b:ForwardingPath)=> { + return a.name.localeCompare(b.name); + }); + this.onAddServicePath = this.input.onCreateServicePath; + this.onEditServicePath = this.input.onEditServicePath; + } + + deletePath = (id:string):void => { + this.serviceService.deleteServicePath(this.input.service, id).subscribe((res:any) => { + delete this.input.service.forwardingPaths[id]; + this.paths = this.paths.filter(function(path){ + return path.uniqueId !== id; + }); + }); + }; + +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts new file mode 100644 index 0000000000..c236934002 --- /dev/null +++ b/catalog-ui/src/app/ng2/pages/service-paths-list/service-paths-list.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from "@angular/core"; +import {CommonModule} from "@angular/common"; +import ServicePathsListComponent from "./service-paths-list.component"; + +@NgModule({ + declarations: [ + ServicePathsListComponent + ], + imports: [CommonModule], + exports: [], + entryComponents: [ + ServicePathsListComponent + ], + providers: [] +}) +export class ServicePathsListModule { +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts index e89a816e95..3761aa808f 100644 --- a/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts +++ b/catalog-ui/src/app/ng2/pipes/global-pipes.module.ts @@ -13,7 +13,6 @@ import {SafeUrlSanitizerPipe} from "./safeUrlSanitizer.pipe"; SearchFilterPipe, SafeUrlSanitizerPipe ], - exports: [ ContentAfterLastDotPipe, GroupByPipe, diff --git a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts index b852539edd..0947b2aa7f 100644 --- a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts +++ b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts @@ -23,10 +23,9 @@ import {Response, RequestOptions, Headers} from '@angular/http'; import { Observable } from 'rxjs/Observable'; import {PropertyFEModel, PropertyBEModel} from "app/models"; import {CommonUtils} from "app/utils"; -import {Component, ComponentInstance, InputModel} from "app/models"; +import {Component, ComponentInstance, Capability, PropertyModel} from "app/models"; import { HttpService } from '../http.service'; import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; -import {isEqual} from "lodash"; @Injectable() export class ComponentInstanceServiceNg2 { @@ -52,43 +51,45 @@ export class ComponentInstanceServiceNg2 { }) } - updateInstanceProperty(component: Component, componentInstanceId: string, property: PropertyBEModel): Observable<PropertyBEModel> { + updateInstanceProperties(component: Component, componentInstanceId: string, properties: PropertyBEModel[]) { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/property', property) + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/properties', properties) .map((res: Response) => { - return new PropertyBEModel(res.json()); - }) + return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + }); } - getInstanceCapabilityProperties(component: Component, componentInstanceId: string, capabilityType: string, capabilityName: string): Observable<Array<PropertyBEModel>> { + getInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability): Observable<Array<PropertyModel>> { - return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capabilityType + - '/capabilityName/' + capabilityName + '/properties') + return this.http.get(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + + '/capabilityName/' + capability.name + '/ownerId/' + capability.ownerId + '/properties') .map((res: Response) => { - return CommonUtils.initBeProperties(res.json()); + capability.properties = res.json().map((capProp) => new PropertyModel(capProp)); // update capability properties + return capability.properties; }) } - updateInstanceCapabilityProperties(component: Component, componentInstanceId: string, capabilityType: string, capabilityName: string, properties: PropertyBEModel[]): Observable<PropertyBEModel[]> { + updateInstanceCapabilityProperties(component: Component, componentInstanceId: string, capability: Capability, properties: PropertyBEModel[]): Observable<Array<PropertyModel>> { - return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capabilityType + - '/capabilityName/' + capabilityName +'/properties', properties) + return this.http.put(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/componentInstances/' + componentInstanceId + '/capability/' + capability.type + + '/capabilityName/' + capability.name + '/ownerId/' + capability.ownerId + '/properties', properties) .map((res: Response) => { - return res.json().map((resProperty) => new PropertyBEModel(resProperty)); + const savedProperties: PropertyModel[] = res.json().map((resProperty) => new PropertyModel(resProperty)); + savedProperties.forEach((savedProperty) => { + const propIdx = capability.properties.findIndex((p) => p.uniqueId === savedProperty.uniqueId); + if (propIdx !== -1) { + capability.properties.splice(propIdx, 1, savedProperty); + } + }); + return savedProperties; }) } - updateInstanceInput(component: Component, componentInstanceId: string, input: PropertyBEModel): Observable<PropertyBEModel> { + updateInstanceInputs(component: Component, componentInstanceId: string, inputs: PropertyBEModel[]): Observable<PropertyBEModel[]> { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/input', input) + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/inputs', inputs) .map((res: Response) => { - return new PropertyBEModel(res.json()); - }) - } - - hasPropertyChanged(property: PropertyFEModel) { - let oldValue: any = property.value; - const newValue = property.getJSONValue(); - return ((oldValue || newValue) && !isEqual(oldValue, newValue)); + return res.json().map((resInput) => new PropertyBEModel(resInput)); + }); } } diff --git a/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts b/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts new file mode 100644 index 0000000000..6e9d0e8031 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/component-services/component.service.factory.ts @@ -0,0 +1,41 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + + +import {Injectable} from "@angular/core"; +import {Component} from "../../../models/components/component"; +import {ComponentServiceNg2} from "./component.service"; +import {ServiceServiceNg2} from "./service.service"; + +@Injectable() +export class ComponentServiceFactoryNg2 { + componentService: ComponentServiceNg2; + serviceService: ServiceServiceNg2; + constructor(componentService: ComponentServiceNg2, serviceService: ServiceServiceNg2) { + this.serviceService = serviceService; + this.componentService = componentService; + } + getComponentService(component: Component):ComponentServiceNg2 { + if (component.isService()) { + return this.serviceService; + } + return this.componentService; + } +}
\ 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 ba1cb15561..9c3f78a444 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 @@ -18,13 +18,13 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {Injectable, Inject} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; import {Response, URLSearchParams} from '@angular/http'; -import { Component, PropertyBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData} from "app/models"; -import {downgradeInjectable} from '@angular/upgrade/static'; +import { Component, InputBEModel, InstancePropertiesAPIMap, FilterPropertiesAssignmentData} 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"; @@ -40,11 +40,11 @@ export class ComponentServiceNg2 { protected baseUrl; - constructor(private http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(protected http:HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } - private getComponentDataByFieldsName(componentType:string, componentId: string, fields:Array<string>):Observable<ComponentGenericResponse> { + protected getComponentDataByFieldsName(componentType:string, componentId: string, fields:Array<string>):Observable<ComponentGenericResponse> { let params:URLSearchParams = new URLSearchParams(); _.forEach(fields, (field:string):void => { @@ -53,10 +53,14 @@ export class ComponentServiceNg2 { return this.http.get(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/filteredDataByParams', {search: params}) .map((res:Response) => { - return new ComponentGenericResponse().deserialize(res.json()); + return this.analyzeComponentDataResponse(res); }); } + protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { + return new ComponentGenericResponse().deserialize(res.json()); + } + private getServerTypeUrl = (componentType:string):string => { switch (componentType) { case ComponentType.SERVICE: @@ -78,8 +82,8 @@ export class ComponentServiceNg2 { return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_ATTRIBUTES]); } - getComponentInstancesAndRelation(component:Component):Observable<ComponentGenericResponse> { - return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES]); + getComponentCompositionData(component:Component):Observable<ComponentGenericResponse> { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, COMPONENT_FIELDS.COMPONENT_POLICIES, COMPONENT_FIELDS.COMPONENT_GROUPS]); } getComponentResourceInstances(component:Component):Observable<ComponentGenericResponse> { @@ -125,19 +129,19 @@ export class ComponentServiceNg2 { }) } - deleteInput(component:Component, input:PropertyBEModel):Observable<PropertyBEModel> { + deleteInput(component:Component, input:InputBEModel):Observable<InputBEModel> { return this.http.delete(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/delete/' + input.uniqueId + '/input') .map((res:Response) => { - return new PropertyBEModel(res.json()); + return new InputBEModel(res.json()); }) } - updateComponentInput(component:Component, input:PropertyBEModel):Observable<PropertyBEModel> { + updateComponentInputs(component:Component, inputs:InputBEModel[]):Observable<InputBEModel[]> { - return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', input) + return this.http.post(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/update/inputs', inputs) .map((res:Response) => { - return new PropertyBEModel(res.json()) + return res.json().map((input) => new InputBEModel(input)); }) } diff --git a/catalog-ui/src/app/ng2/services/component-services/service.service.ts b/catalog-ui/src/app/ng2/services/component-services/service.service.ts index f38dbef595..0439f2047e 100644 --- a/catalog-ui/src/app/ng2/services/component-services/service.service.ts +++ b/catalog-ui/src/app/ng2/services/component-services/service.service.ts @@ -22,19 +22,30 @@ import { Injectable, Inject } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; import 'rxjs/add/operator/toPromise'; -import { Response } from '@angular/http'; +import { Response, URLSearchParams } from '@angular/http'; import {Service} from "app/models"; import { downgradeInjectable } from '@angular/upgrade/static'; import { HttpService } from '../http.service'; + import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; +import {ForwardingPath} from "app/models/forwarding-path"; +import {ComponentMetadata} from "app/models/component-metadata"; +import {ComponentType} from "app/utils"; +import {Component} from "app/models/components/component"; +import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; +import {COMPONENT_FIELDS, SERVICE_FIELDS} from "app/utils/constants"; +import {ComponentServiceNg2} from "./component.service"; +import {ServiceGenericResponse} from "app/ng2/services/responses/service-generic-response"; +import {ServicePathMapItem} from "app/models/graph/nodes-and-links-map"; @Injectable() -export class ServiceServiceNg2 { +export class ServiceServiceNg2 extends ComponentServiceNg2 { protected baseUrl = ""; - constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + constructor(protected http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + super(http, sdcConfig); this.baseUrl = sdcConfig.api.root + sdcConfig.api.component_api_root; } @@ -46,4 +57,106 @@ export class ServiceServiceNg2 { }); } + getNodesAndLinksMap(service: Service):Observable<Array<ServicePathMapItem>> { + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/linksMap').map(res => { + return <Array<ServicePathMapItem>>res.json(); + }); + } + + getServicePath(service: Service, id: string):Observable<any> { + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id) + .map(res => { + return res.json(); + }) + } + + getServicePaths(service: Service):Observable<any> { + return this.http.get(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths') + .map(res => { + return res.json(); + }) + } + + createOrUpdateServicePath(service: Service, inputsToCreate: ForwardingPath):Observable<ForwardingPath> { + if (inputsToCreate.uniqueId) { + return this.updateServicePath(service, inputsToCreate); + } else { + return this.createServicePath(service, inputsToCreate); + } + } + + createServicePath(service: Service, inputsToCreate: ForwardingPath):Observable<ForwardingPath> { + let input = new ServicePathRequestData(inputsToCreate); + + return this.http.post(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input) + .map(res => { + return this.parseServicePathResponse(res); + }); + } + + deleteServicePath(service: Service, id: string):Observable<any> { + return this.http.delete(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths/' + id ) + .map((res) => { + return res.json(); + }); + } + + updateServicePath(service: Service, inputsToUpdate:ForwardingPath):Observable<ForwardingPath> { + let input = new ServicePathRequestData(inputsToUpdate); + + return this.http.put(this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths', input) + .map((res) => { + return this.parseServicePathResponse(res); + }); + } + + checkComponentInstanceVersionChange(service: Service, newVersionId: string):Observable<Array<string>> { + let instanceId = service.selectedInstance.uniqueId; + let queries = {componentInstanceId: instanceId, newComponentInstanceId: newVersionId}; + + let params:URLSearchParams = new URLSearchParams(); + _.map(_.keys(queries), (key:string):void => { + params.append(key, queries[key]); + }); + + let url = this.baseUrl + service.getTypeUrl() + service.uniqueId + '/paths-to-delete'; + return this.http.get(url, {search: params}).map((res: Response) => { + return res.json().forwardingPathToDelete; + }); + } + + getComponentCompositionData(component:Component):Observable<ComponentGenericResponse> { + return this.getComponentDataByFieldsName(component.componentType, component.uniqueId, [COMPONENT_FIELDS.COMPONENT_INSTANCES_RELATION, COMPONENT_FIELDS.COMPONENT_INSTANCES, SERVICE_FIELDS.FORWARDING_PATHS]); + } + + protected analyzeComponentDataResponse(res: Response):ComponentGenericResponse { + return new ServiceGenericResponse().deserialize(res.json()); + } + + private parseServicePathResponse(res: Response):ForwardingPath { + let resJSON = res.json(); + let pathId = Object.keys(resJSON.forwardingPaths)[0]; + let forwardingPath = resJSON.forwardingPaths[pathId]; + let path:ForwardingPath = new ForwardingPath(); + path.deserialize(forwardingPath); + path.uniqueId = pathId; + return path; + } } + +class ServicePathRequestData { + forwardingPaths: { [key:string]:ForwardingPath } = {}; + componentMetadataDefinition: ComponentMetadata; + toscaType: string = "topology_template"; + + constructor(fp? : ForwardingPath) { + this.componentMetadataDefinition = new ComponentMetadata(); + this.componentMetadataDefinition.ecompGeneratedNaming = true; + this.componentMetadataDefinition.componentType = ComponentType.SERVICE; + if (fp) { + let id = fp.uniqueId ? fp.uniqueId : "NEW"; + this.forwardingPaths[fp.uniqueId] = fp; + } + } +} + diff --git a/catalog-ui/src/app/ng2/services/config.service.ts b/catalog-ui/src/app/ng2/services/config.service.ts index 053f2c7659..3e6e667285 100644 --- a/catalog-ui/src/app/ng2/services/config.service.ts +++ b/catalog-ui/src/app/ng2/services/config.service.ts @@ -37,8 +37,8 @@ export class ConfigService { public api:IApi; constructor(private http: Http, @Inject(SdcConfigToken) private sdcConfig:ISdcConfig) { - this.api = this.sdcConfig.api; - this.baseUrl = this.api.root + this.api.component_api_root; + this.api = this.sdcConfig.api; + this.baseUrl = this.sdcConfig.api.root + this.sdcConfig.api.component_api_root; } loadValidationConfiguration(): Promise<ValidationConfiguration> { diff --git a/catalog-ui/src/app/ng2/services/data-type.service.ts b/catalog-ui/src/app/ng2/services/data-type.service.ts index 30c02a4141..6b5908903e 100644 --- a/catalog-ui/src/app/ng2/services/data-type.service.ts +++ b/catalog-ui/src/app/ng2/services/data-type.service.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { DataTypeModel, DataTypesMap, PropertyBEModel, PropertyFEModel, DerivedFEProperty, DerivedFEPropertyMap } from "app/models"; import { DataTypesService } from "app/services/data-types-service"; diff --git a/catalog-ui/src/app/ng2/services/dynamic-component.service.ts b/catalog-ui/src/app/ng2/services/dynamic-component.service.ts new file mode 100644 index 0000000000..29dd1e9e09 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/dynamic-component.service.ts @@ -0,0 +1,28 @@ +import { + Injectable, Type, ViewContainerRef, ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef +} from '@angular/core'; + + + +@Injectable() +export class DynamicComponentService { + + constructor(private componentFactoryResolver: ComponentFactoryResolver, private applicationRef: ApplicationRef) { } + + //Creates a component dynamically (aka during runtime). If a view container is not specified, it will append the new component to the app root. + //To subscribe to an event from invoking component: componentRef.instance.clicked.subscribe((m) => console.log(m.name)); + public createDynamicComponent<T>(componentType: Type<T>, viewContainerRef?:ViewContainerRef): ComponentRef<T> { + + viewContainerRef = viewContainerRef || this.getRootViewContainerRef(); + viewContainerRef.clear(); + + let factory: ComponentFactory<T> = this.componentFactoryResolver.resolveComponentFactory(componentType); //Ref: https://angular.io/guide/dynamic-component-loader + let componentRef: ComponentRef<T> = viewContainerRef.createComponent(factory); + return componentRef; + } + + + private getRootViewContainerRef(): ViewContainerRef { + return this.applicationRef.components[0].instance.viewContainerRef; + } +};
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/services/modal.service.ts b/catalog-ui/src/app/ng2/services/modal.service.ts index 22b56c7004..4e86d6accf 100644 --- a/catalog-ui/src/app/ng2/services/modal.service.ts +++ b/catalog-ui/src/app/ng2/services/modal.service.ts @@ -1,11 +1,12 @@ import { Injectable, Type, ViewContainerRef, ApplicationRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, - + TemplateRef } from '@angular/core'; import { ModalModel, ButtonModel, StepModel } from 'app/models'; import {MultiStepsWizardComponent} from "../components/ui/multi-steps-wizard/multi-steps-wizard.component"; import {ModalComponent} from "../components/ui/modal/modal.component"; import {WizardHeaderBaseComponent} from "app/ng2/components/ui/multi-steps-wizard/multi-steps-wizard-header-base.component"; +import { DynamicComponentService } from 'app/ng2/services/dynamic-component.service'; @Injectable() @@ -13,7 +14,7 @@ export class ModalService { currentModal: ComponentRef<any>; - constructor(private componentFactoryResolver: ComponentFactoryResolver, private applicationRef: ApplicationRef) { } + constructor(private dynamicComponentService: DynamicComponentService) { } /* Shortcut method to open an alert modal with title, message, and close button that simply closes the modal. */ @@ -52,7 +53,7 @@ export class ModalService { /* Use this method to create a modal with title, message, and completely custom buttons. Use response.instance.open() to open */ public createCustomModal = (customModalData: ModalModel): ComponentRef<ModalComponent> => { - let customModal: ComponentRef<ModalComponent> = this.createDynamicComponent(ModalComponent); + let customModal: ComponentRef<ModalComponent> = this.dynamicComponentService.createDynamicComponent(ModalComponent); customModal.instance.input = customModalData; this.currentModal = customModal; @@ -62,12 +63,12 @@ export class ModalService { public createMultiStepsWizard = (title: string, steps:Array<StepModel>, callback: Function, dynamicHeaderType?: Type<WizardHeaderBaseComponent>): ComponentRef<MultiStepsWizardComponent> => { let cancelButton: ButtonModel = new ButtonModel('Cancel', 'outline blue', this.closeCurrentModal); let modalModel: ModalModel = new ModalModel('xl', title, '', [cancelButton]); - let wizardInstance: ComponentRef<MultiStepsWizardComponent> = this.createDynamicComponent(MultiStepsWizardComponent); + let wizardInstance: ComponentRef<MultiStepsWizardComponent> = this.dynamicComponentService.createDynamicComponent(MultiStepsWizardComponent); wizardInstance.instance.input = modalModel; wizardInstance.instance.steps = steps; wizardInstance.instance.callback = callback; if(dynamicHeaderType){ - let dynamicHeader = this.createDynamicComponent(dynamicHeaderType, wizardInstance.instance.dynamicHeaderContainer); + let dynamicHeader = this.dynamicComponentService.createDynamicComponent(dynamicHeaderType, wizardInstance.instance.dynamicHeaderContainer); wizardInstance.instance.dynamicHeader = dynamicHeader; wizardInstance.instance.dynamicHeader.instance.currentStepIndex = 0; } @@ -76,38 +77,28 @@ export class ModalService { return wizardInstance; } - + public closeCurrentModal = () => { if (!this.currentModal) return; this.currentModal.instance.close(); this.currentModal.destroy(); + delete this.currentModal; } public addDynamicContentToModal = (modalInstance: ComponentRef<ModalComponent>, dynamicComponentType: Type<any>, dynamicComponentInput?: any) => { - let dynamicContent = this.createDynamicComponent(dynamicComponentType, modalInstance.instance.dynamicContentContainer); + let dynamicContent = this.dynamicComponentService.createDynamicComponent(dynamicComponentType, modalInstance.instance.dynamicContentContainer); dynamicContent.instance.input = dynamicComponentInput; modalInstance.instance.dynamicContent = dynamicContent; return modalInstance; } - //Creates a component dynamically (aka during runtime). If a view container is not specified, it will append the new component to the app root. - //To subscribe to an event from invoking component: componentRef.instance.clicked.subscribe((m) => console.log(m.name)); - private createDynamicComponent<T>(componentType: Type<T>, viewContainerRef?:ViewContainerRef): ComponentRef<T> { - - viewContainerRef = viewContainerRef || this.getRootViewContainerRef(); - viewContainerRef.clear(); + public addDynamicTemplateToModal = (modalInstance: ComponentRef<ModalComponent>, templateRef: TemplateRef<void>) => { + modalInstance.instance.dynamicContentContainer.clear(); + modalInstance.instance.dynamicContentContainer.createEmbeddedView(templateRef); + return modalInstance; + }; - let factory: ComponentFactory<T> = this.componentFactoryResolver.resolveComponentFactory(componentType); //Ref: https://angular.io/guide/dynamic-component-loader - let componentRef: ComponentRef<T> = viewContainerRef.createComponent(factory); - return componentRef; - } - - private getRootViewContainerRef(): ViewContainerRef { - return this.applicationRef.components[0].instance.viewContainerRef; - } } - - diff --git a/catalog-ui/src/app/ng2/services/policies.service.ts b/catalog-ui/src/app/ng2/services/policies.service.ts new file mode 100644 index 0000000000..2b564b8915 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/policies.service.ts @@ -0,0 +1,49 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +import { Injectable, Inject } from "@angular/core"; +import { Headers } from "@angular/http"; +import { Observable } from "rxjs/Observable"; +import { HttpService } from "./http.service"; +import { Cookie2Service } from "./cookie.service"; +import {SdcConfigToken, ISdcConfig} from "../config/sdc-config.config"; + + +@Injectable() +export class PoliciesService { + protected baseUrl; + + private mapApiDirections = { + 'RESOURCE':'resources', + 'SERVICE':'services' + } + + constructor(private http: HttpService, @Inject(SdcConfigToken) sdcConfig:ISdcConfig) { + this.baseUrl = sdcConfig.api.root ; + } + + public createPolicyInstance(entityType:string, id:string, policyType:string) { + return this.http.post(this.baseUrl + '/v1/catalog/' + this.mapApiDirections[entityType] + '/' + id +'/policies/' + policyType, {}).map(resp => { + return resp.json(); + }); + } + +} + diff --git a/catalog-ui/src/app/ng2/services/properties.service.ts b/catalog-ui/src/app/ng2/services/properties.service.ts index 86cd2f5c72..c86d207915 100644 --- a/catalog-ui/src/app/ng2/services/properties.service.ts +++ b/catalog-ui/src/app/ng2/services/properties.service.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import { Injectable } from '@angular/core'; import { PropertyFEModel, PropertyBEModel, PropertyDeclareAPIModel, DerivedFEProperty} from "app/models"; 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 9450e4bc04..e7c88a0ab8 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 @@ -27,6 +27,7 @@ import { ArtifactGroupModel, PropertyModel, PropertiesGroup, AttributeModel, Att import {CommonUtils} from "app/utils"; import {Serializable} from "../utils/serializable"; import {PropertyBEModel} from "../../../models/properties-inputs/property-be-model"; +import { PolicyInstance } from "app/models/graph/zones/policy-instance"; export class ComponentGenericResponse implements Serializable<ComponentGenericResponse> { @@ -43,6 +44,7 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR public requirements:RequirementsGroup; public properties:Array<PropertyModel>; public attributes:Array<AttributeModel>; + public policies:Array<PolicyInstance>; public groups:Array<Module>; public interfaces:any; public additionalInformation:any; @@ -92,6 +94,9 @@ export class ComponentGenericResponse implements Serializable<ComponentGenericR if(response.groups) { this.groups = CommonUtils.initModules(response.groups); } + if(response.policies) { + this.policies = CommonUtils.initPolicies(response.policies); + } return this; } } diff --git a/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts b/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts new file mode 100644 index 0000000000..d32ed26bb2 --- /dev/null +++ b/catalog-ui/src/app/ng2/services/responses/service-generic-response.ts @@ -0,0 +1,22 @@ +import * as _ from "lodash"; +import {Serializable} from "../utils/serializable"; +import {ComponentGenericResponse} from "./component-generic-response"; +import {ForwardingPath} from "../../../models/forwarding-path"; + +export class ServiceGenericResponse extends ComponentGenericResponse implements Serializable<ServiceGenericResponse> { + public forwardingPaths: { [key:string]:ForwardingPath } = {}; + + deserialize (response): ServiceGenericResponse { + super.deserialize(response); + if(response.forwardingPaths) { + _.forEach(response.forwardingPaths, (pathResponse, id) => { + let pathId = id; + let path:ForwardingPath = new ForwardingPath(); + path.deserialize(pathResponse); + path.uniqueId = pathId; + this.forwardingPaths[pathId] = path; + }); + } + return this; + } +}
\ No newline at end of file diff --git a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts index 51314c04bd..c005efcbe8 100644 --- a/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts +++ b/catalog-ui/src/app/ng2/utils/ng1-upgraded-provider.ts @@ -27,6 +27,7 @@ import {SharingService} from "../../services/sharing-service"; import {CookieService} from "../../services/cookie-service"; import {CacheService} from "../../services/cache-service"; import {EventListenerService} from "app/services/event-listener-service"; +import IScope = angular.IScope; /** Services we need to upgrade from angular1 to angular2 - in the future we need to rewrite them all to angular2 **/ @@ -62,6 +63,10 @@ export function eventListenerServiceServiceFactory(cacheObj: ICacheObject) { return cacheObj.get('EventListenerService'); } +export function notificationServiceFactory(cacheObj: ICacheObject) { + return cacheObj.get('Notification'); +} + export const DataTypesServiceProvider = { provide: DataTypesService, useFactory: dataTypesServiceFactory, @@ -111,3 +116,9 @@ export const EventListenerServiceProvider = { useFactory: eventListenerServiceServiceFactory, deps: ['$injector'] }; + +export const NotificationServiceProvider = { + provide: 'Notification', + useFactory: notificationServiceFactory, + deps: ['$injector'] +}; diff --git a/catalog-ui/src/app/services/components/component-service.ts b/catalog-ui/src/app/services/components/component-service.ts index 1b2b9f2fa1..74166a0c9f 100644 --- a/catalog-ui/src/app/services/components/component-service.ts +++ b/catalog-ui/src/app/services/components/component-service.ts @@ -18,6 +18,7 @@ * ============LICENSE_END========================================================= */ 'use strict'; +import * as _ from "lodash"; import {ArtifactModel, IFileDownload, InstancesInputsPropertiesMap, InputModel, IValidate, RelationshipModel, PropertyModel, Component, ComponentInstance, AttributeModel, IAppConfigurtaion, Resource, Module, DisplayModule, ArtifactGroupModel, InputsAndProperties} from "app/models"; import {ComponentInstanceFactory, CommonUtils} from "app/utils"; @@ -40,6 +41,7 @@ export interface IComponentService { updateAttribute(componentId:string, attribute:AttributeModel):ng.IPromise<AttributeModel>; deleteProperty(componentId:string, propertyId:string):ng.IPromise<PropertyModel>; deleteAttribute(componentId:string, attributeId:string):ng.IPromise<AttributeModel>; + checkResourceInstanceVersionChange(componentId:string, componentInstanceId:string, componentUid:string):ng.IPromise<any>; changeResourceInstanceVersion(componentId:string, componentInstanceId:string, componentUid:string):ng.IPromise<ComponentInstance>; updateInstanceArtifact(componentId:string, instanceId:string, artifact:ArtifactModel):ng.IPromise<ArtifactModel>; addInstanceArtifact(componentId:string, instanceId:string, artifact:ArtifactModel):ng.IPromise<ArtifactModel>; @@ -55,7 +57,7 @@ export interface IComponentService { deleteRelation(componentId:string, link:RelationshipModel):ng.IPromise<RelationshipModel>; fetchRelation(componentId:string, linkId:string):ng.IPromise<RelationshipModel>; getRequirementsCapabilities(componentId:string):ng.IPromise<any>; - updateInstanceProperty(componentId:string, property:PropertyModel):ng.IPromise<PropertyModel>; + updateInstanceProperties(componentId:string, componentInstanceId:string, properties:PropertyModel[]):ng.IPromise<PropertyModel[]>; updateInstanceAttribute(componentId:string, attribute:AttributeModel):ng.IPromise<AttributeModel>; getComponentInstancesFilteredByInputsAndProperties(componentId:string, searchText:string):ng.IPromise<Array<ComponentInstance>> getComponentInstanceInputs(componentId:string, instanceId:string, originComponentUid):ng.IPromise<Array<InputModel>>; @@ -422,6 +424,16 @@ export class ComponentService implements IComponentService { return deferred.promise; }; + public checkResourceInstanceVersionChange = (componentId:string, componentInstanceId:string, componentUid:string):ng.IPromise<ComponentInstance> => { + let deferred = this.$q.defer(); + this.restangular.one(componentId).one("resourceInstance").one(componentInstanceId).one(componentUid).one("checkForwardingPathOnVersionChange").get().then((response:any) => { + deferred.resolve(response); + }, err => { + deferred.reject(err); + }); + return deferred.promise; + }; + public changeResourceInstanceVersion = (componentId:string, componentInstanceId:string, componentUid:string):ng.IPromise<ComponentInstance> => { let deferred = this.$q.defer(); this.restangular.one(componentId).one("resourceInstance").one(componentInstanceId).one("changeVersion").customPOST({'componentUid': componentUid}).then((response:any) => { @@ -498,14 +510,16 @@ export class ComponentService implements IComponentService { return deferred.promise; }; - public updateInstanceProperty = (componentId:string, property:PropertyModel):ng.IPromise<PropertyModel> => { - let deferred = this.$q.defer(); - let instanceId = property.resourceInstanceUniqueId; - this.restangular.one(componentId).one("resourceInstance").one(instanceId).one("property").customPOST(JSON.stringify(property)).then((response:any) => { - let newProperty = new PropertyModel(response); - newProperty.readonly = true; - newProperty.resourceInstanceUniqueId = instanceId; - deferred.resolve(newProperty); + public updateInstanceProperties = (componentId:string, componentInstanceId:string, properties:PropertyModel[]):ng.IPromise<PropertyModel[]> => { + let deferred = this.$q.defer<PropertyModel[]>(); + this.restangular.one(componentId).one("resourceInstance").one(componentInstanceId).one("properties").customPOST(JSON.stringify(properties)).then((response:any) => { + const newProperties = response.map((res) => { + const newProperty = new PropertyModel(res); + newProperty.readonly = true; + newProperty.resourceInstanceUniqueId = componentInstanceId; + return newProperty; + }); + deferred.resolve(newProperties); }, (err)=> { deferred.reject(err); }); diff --git a/catalog-ui/src/app/services/components/resource-service.ts b/catalog-ui/src/app/services/components/resource-service.ts index 641d7cba48..aabc14f8bd 100644 --- a/catalog-ui/src/app/services/components/resource-service.ts +++ b/catalog-ui/src/app/services/components/resource-service.ts @@ -22,6 +22,7 @@ * Created by obarda on 2/4/2016. */ 'use strict'; +import * as _ from "lodash"; import {IComponentService, ComponentService} from "./component-service"; import {PropertyModel, IAppConfigurtaion, Resource, Component} from "../../models"; import {SharingService} from "../sharing-service"; diff --git a/catalog-ui/src/app/services/components/service-service.ts b/catalog-ui/src/app/services/components/service-service.ts index 4229c4aa2a..d55cae75ce 100644 --- a/catalog-ui/src/app/services/components/service-service.ts +++ b/catalog-ui/src/app/services/components/service-service.ts @@ -22,6 +22,7 @@ * Created by obarda on 2/4/2016. */ 'use strict'; +import * as _ from "lodash"; import {IComponentService, ComponentService} from "./component-service"; import {Distribution, DistributionComponent, Service, PropertyModel, Component, IAppConfigurtaion} from "app/models"; import {SharingService} from "../sharing-service"; diff --git a/catalog-ui/src/app/services/components/utils/composition-left-palette-service.ts b/catalog-ui/src/app/services/components/utils/composition-left-palette-service.ts index a1fa65159f..2ed995c78c 100644 --- a/catalog-ui/src/app/services/components/utils/composition-left-palette-service.ts +++ b/catalog-ui/src/app/services/components/utils/composition-left-palette-service.ts @@ -21,25 +21,28 @@ * Created by obarda on 3/13/2016. */ 'use strict'; -import {LeftPaletteComponent} from "../../../models/components/displayComponent"; -import {Component} from "../../../models/components/component"; +import * as _ from "lodash"; +import {LeftPaletteComponent, LeftPaletteMetadataTypes} from "app/models/components/displayComponent"; +import {Component} from "app/models/components/component"; import {EventListenerService} from "../../event-listener-service"; import {ComponentFactory} from "../../../utils/component-factory"; -import {IAppConfigurtaion} from "../../../models/app-config"; +import {IAppConfigurtaion} from "app/models/app-config"; import {ResourceType, ComponentType, EVENTS} from "../../../utils/constants"; -import {ComponentMetadata} from "../../../models/component-metadata"; +import {ComponentMetadata} from "app/models/component-metadata"; +import {GroupMetadata, GroupTpes} from "app/models/group-metadata"; +import {PolicyMetadata, PolicyTpes} from "app/models/policy-metadata"; import {Resource} from "app/models/components/resource"; -export class LeftPaletteDataObject { - displayLeftPanelComponents:Array<LeftPaletteComponent>; - onFinishLoadingEvent:string; +// export class LeftPaletteDataObject { +// displayLeftPanelComponents:Array<LeftPaletteComponent>; +// onFinishLoadingEvent:string; - constructor(onFinishEventListener:string) { +// constructor(onFinishEventListener:string) { - this.displayLeftPanelComponents = new Array<LeftPaletteComponent>(); - this.onFinishLoadingEvent = onFinishEventListener; - } -} +// this.displayLeftPanelComponents = new Array<LeftPaletteComponent>(); +// this.onFinishLoadingEvent = onFinishEventListener; +// } +// } export class LeftPaletteLoaderService { @@ -62,55 +65,72 @@ export class LeftPaletteLoaderService { } - private serviceLeftPaletteData:LeftPaletteDataObject; - private resourceLeftPaletteData:LeftPaletteDataObject; - private resourcePNFLeftPaletteData:LeftPaletteDataObject; - private vlData:LeftPaletteDataObject; + // private serviceLeftPaletteData:LeftPaletteDataObject; + // private resourceLeftPaletteData:LeftPaletteDataObject; + // private resourcePNFLeftPaletteData:LeftPaletteDataObject; + // private vlData:LeftPaletteDataObject; + leftPanelComponents:Array<LeftPaletteComponent>; public loadLeftPanel = (component:Component):void => { - this.serviceLeftPaletteData = new LeftPaletteDataObject(EVENTS.SERVICE_LEFT_PALETTE_UPDATE_EVENT); - this.resourceLeftPaletteData = new LeftPaletteDataObject(EVENTS.RESOURCE_LEFT_PALETTE_UPDATE_EVENT); - this.resourcePNFLeftPaletteData = new LeftPaletteDataObject(EVENTS.RESOURCE_PNF_LEFT_PALETTE_UPDATE_EVENT); - this.updateComponentLeftPalette(component); + // this.serviceLeftPaletteData = new LeftPaletteDataObject(EVENTS.SERVICE_LEFT_PALETTE_UPDATE_EVENT); + // this.resourceLeftPaletteData = new LeftPaletteDataObject(EVENTS.RESOURCE_LEFT_PALETTE_UPDATE_EVENT); + // this.resourcePNFLeftPaletteData = new LeftPaletteDataObject(EVENTS.RESOURCE_PNF_LEFT_PALETTE_UPDATE_EVENT); + this.leftPanelComponents = []; + this.updateLeftPaletteForTopologyTemplate(component); } - private getResourceLeftPaletteDataByResourceType = (resourceType:string):LeftPaletteDataObject => { - if(resourceType == ResourceType.PNF) { - return this.resourcePNFLeftPaletteData; - } - return this.resourceLeftPaletteData; - } + // private getResourceLeftPaletteDataByResourceType = (resourceType:string):LeftPaletteDataObject => { + // if(resourceType == ResourceType.PNF) { + // return this.resourcePNFLeftPaletteData; + // } + // return this.resourceLeftPaletteData; + // } - private onFinishLoading = (componentType:string, leftPaletteData:LeftPaletteDataObject):void => { - this.EventListenerService.notifyObservers(leftPaletteData.onFinishLoadingEvent); - }; - - private updateLeftPalette = (componentType, componentInternalType:string, leftPaletteData:LeftPaletteDataObject):void => { + private updateLeftPalette = (componentInternalType:string):void => { + /* add components */ this.restangular.one("resources").one('/latestversion/notabstract/metadata').get({'internalComponentType': componentInternalType}).then((leftPaletteComponentMetadata:Array<ComponentMetadata>) => { _.forEach(leftPaletteComponentMetadata, (componentMetadata:ComponentMetadata) => { - leftPaletteData.displayLeftPanelComponents.push(new LeftPaletteComponent(componentMetadata)); + this.leftPanelComponents.push(new LeftPaletteComponent(LeftPaletteMetadataTypes.Component, componentMetadata)); }); - this.onFinishLoading(componentType, leftPaletteData); + this.EventListenerService.notifyObservers(EVENTS.LEFT_PALETTE_UPDATE_EVENT); }); - }; + + /* add groups */ + //TODO: In backend implement like this: + //this.restangular.one("groups").one('/latestversion/notabstract/metadata').get({'internalComponentType': componentInternalType}).then((leftPaletteComponentMetadata:Array<ComponentMetadata>) => { + this.restangular.one('/groupTypes').get().then((leftPaletteGroupTypes:GroupTpes) => { + _.forEach(leftPaletteGroupTypes.groupTypes, (groupMetadata: GroupMetadata) => { + this.leftPanelComponents.push(new LeftPaletteComponent(LeftPaletteMetadataTypes.Group, groupMetadata)); + }); + this.EventListenerService.notifyObservers(EVENTS.LEFT_PALETTE_UPDATE_EVENT); + }); + + /* add policies */ + //TODO: In backend implement like this: + //this.restangular.one("policies").one('/latestversion/notabstract/metadata').get({'internalComponentType': componentInternalType}).then((leftPaletteComponentMetadata:Array<ComponentMetadata>) => { + this.restangular.one('/policyTypes').get().then((leftPalettePolicyTypes:PolicyTpes) => { + _.forEach(leftPalettePolicyTypes.policyTypes, (policyMetadata: PolicyMetadata) => { + this.leftPanelComponents.push(new LeftPaletteComponent(LeftPaletteMetadataTypes.Policy, policyMetadata)); + }); + this.EventListenerService.notifyObservers(EVENTS.LEFT_PALETTE_UPDATE_EVENT); + }); + } public getLeftPanelComponentsForDisplay = (component:Component):Array<LeftPaletteComponent> => { - switch (component.componentType) { - case ComponentType.SERVICE: - return this.serviceLeftPaletteData.displayLeftPanelComponents; - default://resource - return this.getResourceLeftPaletteDataByResourceType((<Resource>component).resourceType).displayLeftPanelComponents; - } + return this.leftPanelComponents; }; - public updateComponentLeftPalette = (component:Component):void => { + /** + * Update left palete items according to current topology templates we are in. + */ + public updateLeftPaletteForTopologyTemplate = (component:Component):void => { switch (component.componentType) { case ComponentType.SERVICE: - this.updateLeftPalette(ComponentType.SERVICE, ComponentType.SERVICE, this.serviceLeftPaletteData); + this.updateLeftPalette(ComponentType.SERVICE); break; case ComponentType.RESOURCE: - this.updateLeftPalette(ComponentType.RESOURCE, (<Resource>component).resourceType, this.getResourceLeftPaletteDataByResourceType((<Resource>component).resourceType)); + this.updateLeftPalette((<Resource>component).resourceType); break; default: console.log('ERROR: Component type '+ component.componentType + ' is not exists'); diff --git a/catalog-ui/src/app/services/entity-service.ts b/catalog-ui/src/app/services/entity-service.ts index 27b0513b39..b7ac8805ce 100644 --- a/catalog-ui/src/app/services/entity-service.ts +++ b/catalog-ui/src/app/services/entity-service.ts @@ -54,13 +54,13 @@ export class EntityService implements IEntityService { let followedResponse: IComponentsArray = response.data; let componentsList:Array<Component> = new Array(); - followedResponse.services.forEach((serviceResponse:Service) => { + followedResponse.services && followedResponse.services.forEach((serviceResponse:Service) => { let component:Service = this.ComponentFactory.createService(serviceResponse); // new Service(serviceResponse); componentsList.push(component); this.sharingService.addUuidValue(component.uniqueId, component.uuid); }); - followedResponse.resources.forEach((resourceResponse:Resource) => { + followedResponse.resources && followedResponse.resources.forEach((resourceResponse:Resource) => { let component:Resource = this.ComponentFactory.createResource(resourceResponse); componentsList.push(component); this.sharingService.addUuidValue(component.uniqueId, component.uuid); diff --git a/catalog-ui/src/app/services/event-listener-service.ts b/catalog-ui/src/app/services/event-listener-service.ts index 96f9e17641..360edad700 100644 --- a/catalog-ui/src/app/services/event-listener-service.ts +++ b/catalog-ui/src/app/services/event-listener-service.ts @@ -21,6 +21,7 @@ * Created by obarda on 7/4/2016. */ 'use strict'; +import * as _ from "lodash"; import {Dictionary} from "../utils/dictionary/dictionary"; interface IEventListenerService { diff --git a/catalog-ui/src/app/services/onboarding-service.ts b/catalog-ui/src/app/services/onboarding-service.ts index 3dd7269326..3a6e940c6e 100644 --- a/catalog-ui/src/app/services/onboarding-service.ts +++ b/catalog-ui/src/app/services/onboarding-service.ts @@ -75,7 +75,7 @@ export class OnboardingService implements IOnboardingService { }; downloadOnboardingCsar = (packageId:string):ng.IPromise<IFileDownload> => { - let defer = this.$q.defer(); + let defer = this.$q.defer<IFileDownload>(); this.$http({ url: this.api.GET_onboarding + "/" + packageId, method: "get", diff --git a/catalog-ui/src/app/utils/artifacts-utils.ts b/catalog-ui/src/app/utils/artifacts-utils.ts index d6ea616c1e..e99b6411c4 100644 --- a/catalog-ui/src/app/utils/artifacts-utils.ts +++ b/catalog-ui/src/app/utils/artifacts-utils.ts @@ -18,10 +18,12 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {ArtifactModel} from "../models/artifacts"; import {IArtifactResourceFormViewModelScope} from "../view-models/forms/artifact-form/artifact-form-view-model"; import {Component} from "../models/components/component"; import {ArtifactGroupType, ArtifactType} from "./constants"; + export class ArtifactsUtils { static '$inject' = [ diff --git a/catalog-ui/src/app/utils/common-utils.ts b/catalog-ui/src/app/utils/common-utils.ts index 1719034354..97177949ee 100644 --- a/catalog-ui/src/app/utils/common-utils.ts +++ b/catalog-ui/src/app/utils/common-utils.ts @@ -18,9 +18,11 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; import {Module, AttributeModel, ResourceInstance, PropertyModel, InputFEModel} from "../models"; import {ComponentInstanceFactory} from "./component-instance-factory"; import {InputBEModel, PropertyBEModel, RelationshipModel} from "app/models"; +import { PolicyInstance } from "app/models/graph/zones/policy-instance"; export class CommonUtils { @@ -112,5 +114,17 @@ export class CommonUtils { return componentInstancesRelations; } }; + + static initPolicies = (policiesObj: Array<PolicyInstance>):Array<PolicyInstance> => { + let policies = new Array<PolicyInstance>(); + + if (policiesObj) { + _.forEach(policiesObj, (policy: PolicyInstance): void => { + policies.push(new PolicyInstance(policy)); + }) + } + + return policies; + } } diff --git a/catalog-ui/src/app/utils/component-factory.ts b/catalog-ui/src/app/utils/component-factory.ts index f869ef68d3..bf4f23b6eb 100644 --- a/catalog-ui/src/app/utils/component-factory.ts +++ b/catalog-ui/src/app/utils/component-factory.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {DEFAULT_ICON, ResourceType, ComponentType} from "./constants"; import {ServiceService, CacheService, ResourceService} from "app/services"; import {IMainCategory, ISubCategory, ICsarComponent, Component, Resource, Service} from "app/models"; @@ -150,6 +151,7 @@ export class ComponentFactory { case ResourceType.VL: case ResourceType.VFC: case ResourceType.CP: + case ResourceType.CR: case ResourceType.PNF: case ResourceType.CVFC: case ResourceType.CONFIGURATION: diff --git a/catalog-ui/src/app/utils/constants.ts b/catalog-ui/src/app/utils/constants.ts index ea0848eee6..c8603424bb 100644 --- a/catalog-ui/src/app/utils/constants.ts +++ b/catalog-ui/src/app/utils/constants.ts @@ -46,6 +46,7 @@ export class ResourceType { static PNF = 'PNF'; static CVFC = 'CVFC'; static CONFIGURATION = 'Configuration'; + static CR = 'CR'; } export class ComponentState { @@ -189,6 +190,7 @@ export class GraphColors { public static NODE_UCPE_CP = '#9063cd'; public static NODE_UCPE = '#fbfbfb'; public static NODE_SELECTED_BORDER_COLOR = '#30bdf2'; + public static SERVICE_PATH_LINK = '#70208a'; } export class GraphTransactionLogText { public static REMOVE_TEMP_LINK = "remove tempLink"; @@ -244,10 +246,11 @@ export class States { } export class EVENTS { - static RESOURCE_LEFT_PALETTE_UPDATE_EVENT = "resourceLeftPanelUpdateEvent"; - static RESOURCE_PNF_LEFT_PALETTE_UPDATE_EVENT = "resourcePNFLeftPanelUpdateEvent"; - static SERVICE_LEFT_PALETTE_UPDATE_EVENT = "serviceLeftPanelUpdateEvent"; - static VL_LEFT_PALETTE_UPDATE_EVENT = "vlLeftPanelUdateEvent"; + // static RESOURCE_LEFT_PALETTE_UPDATE_EVENT = "resourceLeftPanelUpdateEvent"; + // static RESOURCE_PNF_LEFT_PALETTE_UPDATE_EVENT = "resourcePNFLeftPanelUpdateEvent"; + // static SERVICE_LEFT_PALETTE_UPDATE_EVENT = "serviceLeftPanelUpdateEvent"; + //static VL_LEFT_PALETTE_UPDATE_EVENT = "vlLeftPanelUdateEvent"; + static LEFT_PALETTE_UPDATE_EVENT = "leftPanelUpdateEvent"; static ON_CSAR_LOADING = "onCsarLoading"; static DOWNLOAD_ARTIFACT_FINISH_EVENT = "downloadArtifactFinishEvent"; static ON_WORKSPACE_SAVE_BUTTON_CLICK = "onWorkspaceSaveButtonClick"; @@ -283,6 +286,8 @@ export class GRAPH_EVENTS { static ON_PALETTE_COMPONENT_HOVER_OUT = 'onPaletteComponentHoverOut'; static ON_PALETTE_COMPONENT_DRAG_START = 'onPaletteComponentDragStart'; static ON_PALETTE_COMPONENT_DRAG_ACTION = 'onPaletteComponentDragAction'; + static ON_PALETTE_COMPONENT_SHOW_POPUP_PANEL = 'onPaletteComponentShowPopupPanel'; + static ON_PALETTE_COMPONENT_HIDE_POPUP_PANEL = 'onPaletteComponentHidePopupPanel'; static ON_COMPONENT_INSTANCE_NAME_CHANGED = 'onComponentInstanceNameChanged'; static ON_DELETE_COMPONENT_INSTANCE = 'onDeleteComponentInstance'; static ON_DELETE_MULTIPLE_COMPONENTS = 'onDeleteMultipleComponents'; @@ -291,6 +296,9 @@ export class GRAPH_EVENTS { static ON_REMOVE_NODE_FROM_UCPE = 'onRemoveNodeFromUCPE'; static ON_VERSION_CHANGED = 'onVersionChanged'; static ON_CREATE_COMPONENT_INSTANCE = 'onCreateComponentInstance'; + static ON_ADD_COMPONENT_INSTANCE_ZONE_START = 'onCreateComponentInstanceZone'; + static ON_FINISH_ANIMATION_ZONE = 'onFinishAnimationZone'; + static ON_ZONE_SIZE_CHANGE = 'onZoneSizeChange'; } @@ -308,9 +316,15 @@ export class COMPONENT_FIELDS { static COMPONENT_CAPABILITIES = "capabilities"; static COMPONENT_REQUIREMENTS = "requirements"; static COMPONENT_TOSCA_ARTIFACTS = "toscaArtifacts"; + static COMPONENT_POLICIES = "policies"; static COMPONENT_GROUPS = "groups"; } + +export class SERVICE_FIELDS { + static FORWARDING_PATHS = "forwardingPaths"; +} + export class API_QUERY_PARAMS { static INCLUDE = "include"; } diff --git a/catalog-ui/src/app/utils/menu-handler.ts b/catalog-ui/src/app/utils/menu-handler.ts index d48412a6ed..fc6eb2081a 100644 --- a/catalog-ui/src/app/utils/menu-handler.ts +++ b/catalog-ui/src/app/utils/menu-handler.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {WorkspaceMode, ComponentState} from "./constants"; import {IAppConfigurtaion, IAppMenu, Component} from "../models"; import {ComponentFactory} from "./component-factory"; @@ -92,36 +93,36 @@ export class MenuHandler { } - generateBreadcrumbsModelFromComponents = (components:Array<Component>, selected:Component):MenuItemGroup => { - let result = new MenuItemGroup(0, [], false); - if (components) { + findBreadcrumbComponentIndex = (components:Array<Component>, selected:Component):number => { + let selectedItemIdx; + + // Search the component in all components by uuid (and not uniqueid, gives access to an assets's minor versions). + selectedItemIdx = _.findIndex(components, (item:Component) => { + return item.uuid === selected.uuid; + }); - // Search the component in all components by uuid (and not uniqueid, gives access to an assets's minor versions). - let selectedItem = _.find(components, (item:Component) => { - return item.uuid === selected.uuid; + // If not found search by invariantUUID + if (selectedItemIdx === -1) { + selectedItemIdx = _.findIndex(components, (item:Component) => { + //invariantUUID && Certified State matches between major versions + return item.invariantUUID === selected.invariantUUID && item.lifecycleState === ComponentState.CERTIFIED; }); + } - // If not found search by invariantUUID - if (undefined == selectedItem) { - selectedItem = _.find(components, (item:Component) => { - //invariantUUID && Certified State matches between major versions - return item.invariantUUID === selected.invariantUUID && item.lifecycleState === ComponentState.CERTIFIED; - }); - } + // If not found search by name (name is unique). + if (selectedItemIdx === -1) { + selectedItemIdx = _.findIndex(components, (item:Component) => { + return item.name === selected.name; + }); + } - // If not found search by name (name is unique). - if (undefined == selectedItem) { - selectedItem = _.find(components, (item:Component) => { - return item.name === selected.name; - }); - } - - if(!selectedItem){ - result.selectedIndex = components.length; - }else{ - result.selectedIndex = components.indexOf(selectedItem); - } - components[result.selectedIndex] = selected; + return selectedItemIdx; + }; + + generateBreadcrumbsModelFromComponents = (components:Array<Component>, selected:Component):MenuItemGroup => { + let result = new MenuItemGroup(0, [], false); + if (components) { + result.selectedIndex = this.findBreadcrumbComponentIndex(components, selected); let clickItemCallback = (component:Component):ng.IPromise<boolean> => { this.$state.go('workspace.general', { id: component.uniqueId, diff --git a/catalog-ui/src/app/utils/modals-handler.ts b/catalog-ui/src/app/utils/modals-handler.ts index 0d17683116..48ce487f9d 100644 --- a/catalog-ui/src/app/utils/modals-handler.ts +++ b/catalog-ui/src/app/utils/modals-handler.ts @@ -310,7 +310,7 @@ export class ModalsHandler implements IModalsHandler { }; - openEditModulePropertyModal = (property:PropertyModel, component:Component, selectedModule:DisplayModule):ng.IPromise<any> => { + openEditModulePropertyModal = (property:PropertyModel, component:Component, selectedModule:DisplayModule, filteredProperties:Array<PropertyModel>):ng.IPromise<any> => { let deferred = this.$q.defer(); let modalOptions:ng.ui.bootstrap.IModalSettings = { @@ -328,6 +328,9 @@ export class ModalsHandler implements IModalsHandler { }, selectedModule: ():DisplayModule => { return selectedModule; + }, + filteredProperties: ():Array<PropertyModel> => { + return filteredProperties; } } }; diff --git a/catalog-ui/src/app/utils/validation-utils.ts b/catalog-ui/src/app/utils/validation-utils.ts index 6b45149b1e..cd90ba7752 100644 --- a/catalog-ui/src/app/utils/validation-utils.ts +++ b/catalog-ui/src/app/utils/validation-utils.ts @@ -18,6 +18,8 @@ * ============LICENSE_END========================================================= */ +import * as _ from "lodash"; + class basePattern { pattern:RegExp; base:number; diff --git a/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts index db94a475d1..45232b7a61 100644 --- a/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts +++ b/catalog-ui/src/app/view-models/admin-dashboard/user-management/user-management-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {ModalsHandler} from "app/utils"; import {User, IUserProperties, IUser, IAppConfigurtaion} from "app/models"; import {UserService} from "../../../ng2/services/user.service"; diff --git a/catalog-ui/src/app/view-models/catalog/catalog-view-model.ts b/catalog-ui/src/app/view-models/catalog/catalog-view-model.ts index 10aa47e0ed..2296aa7cdf 100644 --- a/catalog-ui/src/app/view-models/catalog/catalog-view-model.ts +++ b/catalog-ui/src/app/view-models/catalog/catalog-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {Component, IMainCategory, IGroup, IConfigStatuses, IAppMenu, IAppConfigurtaion, IUserProperties, ISubCategory} from "app/models"; import {EntityService, CacheService} from "app/services"; import {ComponentFactory, ResourceType, MenuHandler, ChangeLifecycleStateHandler} from "app/utils"; @@ -149,7 +150,7 @@ export class CatalogViewModel { // Checklist init this.$scope.checkboxes = <Checkboxes>{}; this.$scope.checkboxes.componentTypes = ['Resource', 'Service']; - this.$scope.checkboxes.resourceSubTypes = ['VF', 'VFC', 'PNF', 'CP', 'VL']; + this.$scope.checkboxes.resourceSubTypes = ['VF', 'VFC', 'CR', 'PNF', 'CP', 'VL']; // Checkboxes filter init this.$scope.checkboxesFilter = <CheckboxesFilter>{}; diff --git a/catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts b/catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts index 80a9bcac3e..42628607c9 100644 --- a/catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts +++ b/catalog-ui/src/app/view-models/dashboard/dashboard-view-model.ts @@ -318,6 +318,13 @@ export class DashboardViewModel { }); }; + this.$scope.createCR = ():void => { + this.$state.go('workspace.general', { + type: ComponentType.RESOURCE.toLowerCase(), + resourceType: ResourceType.CR + }); + }; + this.$scope.entitiesCount = (folderItem:FoldersItemsMenu):any => { let self = this; let total:number = 0; diff --git a/catalog-ui/src/app/view-models/dashboard/dashboard-view.html b/catalog-ui/src/app/view-models/dashboard/dashboard-view.html index 0ef5f4a1f9..bddcbcd46c 100644 --- a/catalog-ui/src/app/view-models/dashboard/dashboard-view.html +++ b/catalog-ui/src/app/view-models/dashboard/dashboard-view.html @@ -21,6 +21,7 @@ <div class="w-sdc-dashboard-card-new-content-plus" data-ng-show="!displayActions"></div> <div class="sdc-dashboard-create-element-container" data-ng-show="displayActions"> <button data-ng-if="roles[user.role].dashboard.showCreateNew" data-tests-id="createResourceButton" class="tlv-btn outline blue" data-ng-click="openCreateModal('RESOURCE')">Add VF</button> + <button data-ng-if="roles[user.role].dashboard.showCreateNew" data-tests-id="createCRButton" class="tlv-btn outline blue" data-ng-click="createCR()">Add CR</button> <button data-ng-if="roles[user.role].dashboard.showCreateNew" data-tests-id="createPNFButton" class="tlv-btn outline blue" data-ng-click="createPNF()">Add PNF</button> <button data-ng-if="roles[user.role].dashboard.showCreateNew" data-tests-id="createServiceButton" class="tlv-btn outline blue" data-ng-click="openCreateModal('SERVICE')">Add Service</button> </div> diff --git a/catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts b/catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts index d21011d4b3..19bc548e74 100644 --- a/catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts +++ b/catalog-ui/src/app/view-models/dcae-app/dcae-app-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {MenuItemGroup, MenuItem} from "app/utils"; import {BreadcrumbsPath, BreadcrumbsMenu} from "../onboard-vendor/onboard-vendor-view-model"; import {CacheService} from "app/services"; diff --git a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts index 1306565016..a09e30c330 100644 --- a/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts +++ b/catalog-ui/src/app/view-models/forms/artifact-form/artifact-form-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {ArtifactModel, Resource, Component} from "app/models"; import {ArtifactsUtils, FormState, ValidationUtils, ArtifactType} from "app/utils"; import {CacheService} from "app/services"; diff --git a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts index 4c114b16e6..0e4a851aa6 100644 --- a/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts +++ b/catalog-ui/src/app/view-models/forms/attribute-form/attribute-from-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {AttributeModel, Component} from "app/models"; import {IMapRegex, ValidationUtils, FormState, PROPERTY_TYPES} from "app/utils"; diff --git a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts index 726b61eda2..1f922cdaf9 100644 --- a/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts +++ b/catalog-ui/src/app/view-models/forms/property-forms/base-property-form/property-form-base-model.ts @@ -22,6 +22,7 @@ * Created by obarda on 1/19/2017. */ 'use strict'; +import * as _ from "lodash"; import {DataTypesService} from "app/services/data-types-service"; import {PropertyModel, DataTypesMap, Component} from "app/models"; import {ValidationUtils, PROPERTY_DATA} from "app/utils"; diff --git a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts index 0ad55715db..8ea2e8cf76 100644 --- a/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts +++ b/catalog-ui/src/app/view-models/forms/property-forms/component-property-form/property-form-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import { PROPERTY_TYPES, ModalsHandler, ValidationUtils, PROPERTY_VALUE_CONSTRAINTS, FormState, PROPERTY_DATA} from "app/utils"; import {DataTypesService} from "app/services"; @@ -257,7 +258,7 @@ export class PropertyFormViewModel { let myValueString:string = JSON.stringify(this.$scope.myValue); property.value = myValueString; } - this.component.updateInstanceProperty(property).then(onPropertySuccess, onPropertyFaild); + this.component.updateInstanceProperties(property.resourceInstanceUniqueId, [property]).then((propertiesFromBE) => onPropertySuccess(propertiesFromBE[0]), onPropertyFaild); } else { if (!this.$scope.editPropertyModel.property.simpleType && !this.$scope.isSimpleType(property.type)) { let myValueString:string = JSON.stringify(this.$scope.myValue); diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts index 3a61371e85..510814b333 100644 --- a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts +++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-model.ts @@ -22,6 +22,7 @@ * Created by obarda on 1/18/2017. */ 'use strict'; +import * as _ from "lodash"; import {PropertyModel, DisplayModule, Component, Resource, Service, ComponentInstance} from "app/models"; import {UNIQUE_GROUP_PROPERTIES_NAME} from "app/utils"; import {IPropertyFormBaseViewScope, PropertyFormBaseView} from "../base-property-form/property-form-base-model"; @@ -102,7 +103,9 @@ export class ModulePropertyView extends PropertyFormBaseView { } } else { deferred.resolve(true); - this.$uibModalInstance.close(); + if (isNeedToCloseModal === true) { + this.$uibModalInstance.close(); + } } return deferred.promise; @@ -147,6 +150,13 @@ export class ModulePropertyView extends PropertyFormBaseView { this.initValidation(); + this.$scope.clearValidationError = ():void => { + this.$scope.forms.editForm.$valid = true; + this.$scope.forms.editForm.$invalid = false; + this.$scope.forms.editForm['value'].$error = {}; + this.$scope.forms.editForm.$error = {}; + } + // put default value when instance value is empty this.$scope.onValueChange = ():void => { diff --git a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html index 175f4c199b..ff48cc2357 100644 --- a/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html +++ b/catalog-ui/src/app/view-models/forms/property-forms/module-property-modal/module-property-view.html @@ -12,7 +12,8 @@ name="value" data-ng-pattern="getValidationPattern(property.type)" data-ng-model-options="{ debounce: 200 }" - data-ng-change="onValueChange()" + data-ng-blur="onValueChange()" + data-ng-focus="clearValidationError()" /> <select class="i-sdc-form-select" data-tests-id="booleantype" diff --git a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts index fa027296d6..67d135c575 100644 --- a/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts +++ b/catalog-ui/src/app/view-models/forms/resource-instance-name-form/resource-instance-name-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {ComponentInstanceFactory} from "app/utils"; import {ComponentInstance} from "app/models"; import {Requirement, Component, Capability} from "app/models"; diff --git a/catalog-ui/src/app/view-models/modals/icons-modal/icons-modal-view.ts b/catalog-ui/src/app/view-models/modals/icons-modal/icons-modal-view.ts index eeadcd4e83..155216557f 100644 --- a/catalog-ui/src/app/view-models/modals/icons-modal/icons-modal-view.ts +++ b/catalog-ui/src/app/view-models/modals/icons-modal/icons-modal-view.ts @@ -22,6 +22,7 @@ * Created by rc2122 on 7/4/2017. */ 'use strict'; +import * as _ from "lodash"; import {ComponentFactory} from "app/utils"; import {AvailableIconsService} from "app/services"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; diff --git a/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts index f77a29b003..10f41f6b61 100644 --- a/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts +++ b/catalog-ui/src/app/view-models/onboard-vendor/onboard-vendor-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "underscore"; import {IUserProperties} from "app/models"; import {MenuItemGroup, MenuItem} from "app/utils"; import {CacheService} from "app/services"; diff --git a/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts index 961d5b3677..c438c7ace6 100644 --- a/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts +++ b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view-model.ts @@ -37,7 +37,7 @@ export interface IHierarchyScope extends ng.IScope { onModuleNameChanged(module:DisplayModule):void; updateHeatName():void; loadInstanceModules(instance:ComponentInstance):ng.IPromise<boolean>; - openEditPropertyModal(property:PropertyModel):void; + openEditPropertyModal(property:PropertyModel, filteredProperties:Array<PropertyModel>):void; } export class HierarchyViewModel { @@ -110,8 +110,8 @@ export class HierarchyViewModel { this.$scope.component.updateGroupMetadata(new DisplayModule(this.$scope.selectedModule)).then(onSuccess, onFailed); }; - this.$scope.openEditPropertyModal = (property:PropertyModel):void => { - this.ModalsHandler.openEditModulePropertyModal(property, this.$scope.component, this.$scope.selectedModule).then(() => { + this.$scope.openEditPropertyModal = (property:PropertyModel, filteredProperties:Array<PropertyModel>):void => { + this.ModalsHandler.openEditModulePropertyModal(property, this.$scope.component, this.$scope.selectedModule, filteredProperties).then(() => { }); } } diff --git a/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html index 9eaa3a0f76..3b7b5fc36a 100644 --- a/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html +++ b/catalog-ui/src/app/view-models/tabs/hierarchy/hierarchy-view.html @@ -82,12 +82,12 @@ </div> <expand-collapse-list-header title="Properties" expand-collapse-list-data="expandCollapsePropertiesList"></expand-collapse-list-header> - <div ng-repeat="property in selectedModule.properties | filter: expandCollapsePropertiesList.filter | orderBy:expandCollapsePropertiesList.orderByField track by $index" data-ng-if="expandCollapsePropertiesList.expandCollapse"> + <div ng-repeat="property in filteredProperties=(selectedModule.properties | filter: expandCollapsePropertiesList.filter | orderBy:expandCollapsePropertiesList.orderByField) track by $index" data-ng-if="expandCollapsePropertiesList.expandCollapse"> <div class="list-item property-data" data-ng-class="{'last':$last}"> <div class="property-name module-text-overflow" data-tests-id="selected-module-property-name"> <span tooltips tooltip-content="{{property.name}}" data-ng-class="{'hand': !isViewOnly}" - data-ng-click="!isViewOnly && openEditPropertyModal(property)">{{property.name}}</span> + data-ng-click="!isViewOnly && openEditPropertyModal(property, filteredProperties)">{{property.name}}</span> </div> <div class="module-text-overflow property-info" data-tests-id="selected-module-property-type"> Type: {{property.type}}</div> <div class="module-text-overflow property-info" data-tests-id="selected-module-property-schema-type">Value: {{property.value}}</div> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts index 5d22d65f52..452224a829 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/activity-log/activity-log.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {Activity} from "app/models"; import {ActivityLogService} from "app/services"; 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 5236259f30..46c2d2edf9 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 @@ -18,14 +18,18 @@ * ============LICENSE_END========================================================= */ 'use strict'; +import * as _ from "lodash"; import {Component, ComponentInstance, IAppMenu} from "app/models"; import {SharingService, CacheService, EventListenerService, LeftPaletteLoaderService} from "app/services"; import {ModalsHandler, GRAPH_EVENTS, ComponentFactory, ChangeLifecycleStateHandler, MenuHandler, EVENTS} from "app/utils"; import {IWorkspaceViewModelScope} from "../../workspace-view-model"; -import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service"; import {ComponentGenericResponse} from "app/ng2/services/responses/component-generic-response"; import {Resource} from "app/models/components/resource"; -import {ResourceType,ComponentType} from "../../../../utils/constants"; +import {ResourceType,ComponentType} from "app/utils/constants"; +import {ComponentServiceFactoryNg2} from "app/ng2/services/component-services/component.service.factory"; +import {ServiceGenericResponse} from "app/ng2/services/responses/service-generic-response"; +import {Service} from "app/models/components/service"; + export interface ICompositionViewModelScope extends IWorkspaceViewModelScope { @@ -39,6 +43,8 @@ export interface ICompositionViewModelScope extends IWorkspaceViewModelScope { version:string; isViewOnly:boolean; isLoadingRightPanel:boolean; + disabledTabs:boolean; + openVersionChangeModal(pathsToDelete:string[]):ng.IPromise<any>; onComponentInstanceVersionChange(component:Component); isComponentInstanceSelected():boolean; updateSelectedComponent():void @@ -49,6 +55,7 @@ export interface ICompositionViewModelScope extends IWorkspaceViewModelScope { printScreen():void; isPNF():boolean; isConfiguration():boolean; + preventMoveTab(state: boolean):void; cacheComponentsInstancesFullData:Component; } @@ -70,7 +77,7 @@ export class CompositionViewModel { 'LeftPaletteLoaderService', 'ModalsHandler', 'EventListenerService', - 'ComponentServiceNg2' + 'ComponentServiceFactoryNg2' ]; constructor(private $scope:ICompositionViewModelScope, @@ -87,7 +94,7 @@ export class CompositionViewModel { private LeftPaletteLoaderService:LeftPaletteLoaderService, private ModalsHandler:ModalsHandler, private eventListenerService:EventListenerService, - private ComponentServiceNg2: ComponentServiceNg2) { + private ComponentServiceFactoryNg2: ComponentServiceFactoryNg2) { this.$scope.setValidState(true); this.initScope(); @@ -99,9 +106,14 @@ export class CompositionViewModel { private initGraphData = ():void => { if(!this.$scope.component.componentInstances || !this.$scope.component.componentInstancesRelations ) { this.$scope.isLoading = true; - this.ComponentServiceNg2.getComponentInstancesAndRelation(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + let service = this.ComponentServiceFactoryNg2.getComponentService(this.$scope.component); + service.getComponentCompositionData(this.$scope.component).subscribe((response:ComponentGenericResponse) => { + if (this.$scope.component.isService()) { + (<Service> this.$scope.component).forwardingPaths = (<ServiceGenericResponse>response).forwardingPaths; + } this.$scope.component.componentInstances = response.componentInstances; this.$scope.component.componentInstancesRelations = response.componentInstancesRelations; + this.$scope.component.policies = response.policies; this.$scope.isLoading = false; this.initComponent(); this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPOSITION_GRAPH_DATA_LOADED); @@ -116,17 +128,15 @@ export class CompositionViewModel { private cacheComponentsInstancesFullData:Array<Component>; private initComponent = ():void => { - this.$scope.currentComponent = this.$scope.component; this.$scope.selectedComponent = this.$scope.currentComponent; this.updateUuidMap(); this.$scope.isViewOnly = this.$scope.isViewMode(); }; - private registerGraphEvents = (scope:ICompositionViewModelScope):void => { + private registerGraphEvents = (scope:ICompositionViewModelScope):void => { this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_NODE_SELECTED, scope.setSelectedInstance); this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED, scope.onBackgroundClick); - }; private openUpdateComponentInstanceNameModal = ():void => { @@ -136,7 +146,7 @@ export class CompositionViewModel { }; private removeSelectedComponentInstance = ():void => { - this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_MULTIPLE_COMPONENTS); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, this.$scope.currentComponent.selectedInstance) this.$scope.currentComponent.selectedInstance = null; this.$scope.selectedComponent = this.$scope.currentComponent; }; @@ -151,7 +161,6 @@ export class CompositionViewModel { }; private initScope = ():void => { - this.$scope.sharingService = this.sharingService; this.$scope.sdcMenu = this.sdcMenu; this.$scope.isLoading = false; @@ -231,20 +240,60 @@ export class CompositionViewModel { }; this.$scope.deleteSelectedComponentInstance = ():void => { - let state = "deleteInstance"; - let onOk = ():void => { - this.removeSelectedComponentInstance(); - //this.$scope.graphApi.deleteSelectedNodes(); - }; - let title:string = this.$scope.sdcMenu.alertMessages[state].title; - let message:string = this.$scope.sdcMenu.alertMessages[state].message.format([this.$scope.currentComponent.selectedInstance.name]); - this.ModalsHandler.openAlertModal(title, message).then(onOk); + const {currentComponent} = this.$scope; + const {title, message} = this.$scope.sdcMenu.alertMessages['deleteInstance']; + let modalText = message.format([currentComponent.selectedInstance.name]); + + if (currentComponent.isService()) { + const {forwardingPaths} = (<Service>currentComponent); + const instanceId = currentComponent.selectedInstance.uniqueId; + + const relatedPaths = _.filter(forwardingPaths, forwardingPath => { + const pathElements = forwardingPath.pathElements.listToscaDataDefinition; + return pathElements.find(path => path.fromNode === instanceId || path.toNode === instanceId); + }); + + if (relatedPaths.length) { + const pathNames = _.map(relatedPaths, path => path.name).join(', '); + modalText += `<p>The following service paths will be erased: ${pathNames}</p>`; + } + } + + this.ModalsHandler.openAlertModal(title, modalText).then(this.removeSelectedComponentInstance); + }; + + this.$scope.openVersionChangeModal = (pathsToDelete:string[]):ng.IPromise<any> => { + const {currentComponent} = this.$scope; + const {forwardingPaths} = <Service>currentComponent; + + const relatedPaths = _.filter(forwardingPaths, path => + _.find(pathsToDelete, id => + path.uniqueId === id + ) + ).map(path => path.name); + const pathNames = _.join(relatedPaths, ', ') || 'none'; + + const {title, message} = this.$scope.sdcMenu.alertMessages['upgradeInstance']; + return this.ModalsHandler.openConfirmationModal(title, message.format([pathNames]), false); }; this.$scope.onComponentInstanceVersionChange = (component:Component):void => { - this.$scope.currentComponent = component; - this.$scope.setComponent(this.$scope.currentComponent); - this.$scope.updateSelectedComponent(); + let onChange = () => { + this.$scope.currentComponent = component; + this.$scope.setComponent(this.$scope.currentComponent); + this.$scope.updateSelectedComponent(); + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_VERSION_CHANGED, this.$scope.currentComponent); + }; + + if (component.isService()) { + const service = this.ComponentServiceFactoryNg2.getComponentService(component); + service.getComponentCompositionData(component).subscribe((response:ServiceGenericResponse) => { + (<Service>component).forwardingPaths = response.forwardingPaths; + onChange(); + }); + } else { + onChange(); + } }; this.$scope.isPNF = (): boolean => { @@ -255,6 +304,10 @@ export class CompositionViewModel { return this.$scope.selectedComponent.isResource() && (<Resource>this.$scope.selectedComponent).resourceType === ResourceType.CONFIGURATION; }; + this.$scope.preventMoveTab = (state: boolean): void => { + this.$scope.disabledTabs = state; + }; + this.eventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, this.$scope.reload); } 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 bfb2865348..fceb73b882 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 @@ -5,6 +5,8 @@ is-view-only="isViewOnly" is-loading="isLoading"></palette> + <ng2-palette-popup-panel></ng2-palette-popup-panel> + <composition-graph component="currentComponent" data-tests-id="canvas" is-view-only="isViewOnly" with-sidebar="displayDesignerRightSidebar"></composition-graph> </div> @@ -46,7 +48,8 @@ <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" data-ui-sref="workspace.composition.details" tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information" - data-tests-id="information-tab"> + data-tests-id="information-tab" + data-ng-class="{'disabled': disabledTabs}"> <div class="i-sdc-designer-sidebar-tab-icon sprite-new info"></div> </button> <!--<button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active"--> @@ -58,32 +61,38 @@ data-ui-sref="workspace.composition.deployment" tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Deployment Artifacts" data-tests-id="deployment-artifact-tab" - data-ng-if="!isPNF() && !isConfiguration() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())"> + data-ng-if="!isPNF() && !isConfiguration() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" + data-ng-class="{'disabled': disabledTabs}"> <div class="i-sdc-designer-sidebar-tab-icon sprite-new deployment-artifacts"></div> </button> <button tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="{{selectedComponent.isResource() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy()) ? 'Properties and Attributes': 'Inputs'}}" class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" data-ui-sref="workspace.composition.properties" - data-tests-id="properties-and-attributes-tab"> + data-tests-id="properties-and-attributes-tab" + data-ng-class="{'disabled': disabledTabs}"> <div class="i-sdc-designer-sidebar-tab-icon sprite-new" ng-class="selectedComponent.isResource() || (isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy()) ? 'properties': 'inputs'"></div> </button> <button class="i-sdc-designer-sidebar-tab" data-ui-sref-active="active" data-ui-sref="workspace.composition.artifacts" data-ng-if="!isConfiguration() && !(isComponentInstanceSelected() && currentComponent.selectedInstance.isServiceProxy())" - tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information Artifacts"> + tooltips tooltip-class="tooltip-custom tab-tooltip" tooltip-content="Information Artifacts" + data-ng-class="{'disabled': disabledTabs}"> <div class="i-sdc-designer-sidebar-tab-icon sprite-new information-artifacts"></div> </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" - tooltip-content="Requirements and Capabilities"> + data-tests-id="requirements-and-capabilities" + tooltip-content="Requirements and Capabilities" + data-ng-class="{'disabled': disabledTabs}"> <div class="i-sdc-designer-sidebar-tab-icon sprite-new relations"></div> </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.api" data-tests-id="tab-api" - tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" tooltip-content="API"> + tooltips tooltip-class="tooltip-custom tab-tooltip tooltip-rightside" tooltip-content="API" + data-ng-class="{'disabled': disabledTabs}"> <div class="i-sdc-designer-sidebar-tab-icon sprite-new api"></div> </button> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less index 624ed03431..f351450e6d 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/composition.less @@ -1,3 +1,33 @@ + .i-sdc-designer-leftbar-section-popup-panel { + position: absolute; + display: inline-block; + background-color: white; + border: solid 1px #d2d2d2; + border-top: solid 3px #13a7df; + width: 140px; + height: 40px; + z-index: 10000; + } + + .i-sdc-designer-leftbar-section-popup-panel-group { + padding-left: 8px; + padding-top: 8px; + } + + .i-sdc-designer-leftbar-section-popup-panel-plus { + border-radius: 50%; + color: white; + background-color: #13a7df; + width: 20px; + text-align: center; + display: inline-block; + cursor: pointer; + } + + .i-sdc-designer-leftbar-section-popup-panel-title { + padding-left: 10px; + display: inline-block; + } .composition{ .sdc-workspace-container{ @@ -791,7 +821,16 @@ .g_1; line-height: 18px; } - + + .service-path-buttons { + margin-top: 12px; + position: absolute; + right: 70px; + top: 53px; + &.with-sidebar { + right: 380px; + } + } //Canvas search menu .w-sdc-search-menu { position:absolute; @@ -815,8 +854,8 @@ padding:2px 50px 2px 10px; transition:all 0.4s; } - .clear-search-x { - top: 17px + .search-bar-container { + position:relative; } &:not(:hover):not(.autocomplete-visible):not(.active){ diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts index b80b63cac9..627d20528e 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/artifacts/artifacts-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import { ArtifactModel, Service, @@ -131,6 +132,7 @@ export class ResourceArtifactsViewModel { this.$scope.allowDeleteAndUpdateArtifactMap[artifact.artifactLabel] = this.allowDeleteAndUpdateArtifact(artifact); }); this.$scope.isLoading = false; + this.$scope.preventMoveTab(false); }; @@ -213,6 +215,7 @@ export class ResourceArtifactsViewModel { }; this.$scope.isLoading = true; + this.$scope.preventMoveTab(true); if (this.$scope.isComponentInstanceSelected()) { this.$scope.component.getComponentInstanceArtifactsByGroupType(this.$scope.component.selectedInstance.uniqueId, this.convertToArtifactUrl(this.$scope.artifactType)).then(onGetInstanceArtifactsSuccess, onError); } else { 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 0d8d17ec73..6e3258f69b 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 @@ -19,11 +19,15 @@ */ 'use strict'; +import * as _ from "lodash"; import {Component} from "app/models"; import {GRAPH_EVENTS} from "app/utils"; import {LeftPaletteLoaderService, EventListenerService} from "app/services"; import {ICompositionViewModelScope} from "../../composition-view-model"; import {LeftPaletteComponent} from "../../../../../../models/components/displayComponent"; +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"; export interface IEditResourceVersion { allVersions:any; @@ -45,13 +49,16 @@ export class DetailsViewModel { static '$inject' = [ '$scope', 'LeftPaletteLoaderService', - 'EventListenerService' - + 'EventListenerService', + 'ComponentServiceFactoryNg2', + 'ServiceServiceNg2' ]; constructor(private $scope:IDetailsViewModelScope, private LeftPaletteLoaderService:LeftPaletteLoaderService, - private eventListenerService:EventListenerService) { + private eventListenerService:EventListenerService, + private ComponentServiceFactoryNg2: ComponentServiceFactoryNg2, + private serviceService: ServiceServiceNg2) { this.initScope(); } @@ -112,22 +119,43 @@ export class DetailsViewModel { this.$scope.isLoading = true; this.$scope.$parent.isLoading = true; - let onSuccess = (component:Component)=> { + let service = <Service>this.$scope.currentComponent; + let {changeVersion} = this.$scope.editResourceVersion; + let componentUid:string = this.$scope.editResourceVersion.allVersions[changeVersion]; + + let onCancel = (error:any) => { this.$scope.isLoading = false; this.$scope.$parent.isLoading = false; - this.$scope.onComponentInstanceVersionChange(component); + this.$scope.editResourceVersion.changeVersion = this.$scope.currentComponent.selectedInstance.componentVersion; - this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_VERSION_CHANGED, this.$scope.currentComponent); + if (error) { + console.log(error); + } }; - let onFailed = (error:any)=> { - this.$scope.isLoading = false; - this.$scope.$parent.isLoading = false; - console.log(error); + let onUpdate = () => { + let onSuccess = (component:Component) => { + this.$scope.isLoading = false; + this.$scope.$parent.isLoading = false; + this.$scope.onComponentInstanceVersionChange(component); + }; + + this.$scope.currentComponent.changeComponentInstanceVersion(componentUid).then(onSuccess, onCancel); }; - let componentUid:string = this.$scope.editResourceVersion.allVersions[this.$scope.editResourceVersion.changeVersion]; - this.$scope.currentComponent.changeComponentInstanceVersion(componentUid).then(onSuccess, onFailed); + this.serviceService.checkComponentInstanceVersionChange(service, componentUid).subscribe((pathsToDelete:string[]) => { + if (pathsToDelete && pathsToDelete.length) { + this.$scope.isLoading = false; + this.$scope.$parent.isLoading = false; + this.$scope.$parent.openVersionChangeModal(pathsToDelete).then(() => { + this.$scope.isLoading = true; + this.$scope.$parent.isLoading = true; + onUpdate(); + }, onCancel); + } else { + onUpdate(); + } + }, onCancel); }; } } 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 5020c73df4..033c4668f3 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 @@ -26,7 +26,7 @@ data-ng-if="!isComponentInstanceSelected()" data-tests-id="rightTab_version" data-ng-bind="selectedComponent.version"></span> <ng-form name="editForm" data-ng-if="isComponentInstanceSelected()"> - <select data-ng-model="editResourceVersion.changeVersion" name="changeVersion" data-tests-id="changeVersion" data-ng-disabled="$parent.isViewOnly" + <select data-ng-model="editResourceVersion.changeVersion" name="changeVersion" data-tests-id="changeVersion" data-ng-disabled="$parent.isViewOnly || selectedComponent.uniqueId != editResourceVersion.allVersions[editResourceVersion.changeVersion]" class="i-sdc-designer-sidebar-section-content-item-value i-sdc-form-select" data-ng-class="{'minor': (editResourceVersion.changeVersion)%1}" data-ng-change="changeResourceVersion()"> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts index 99bbd8d7ac..efd5cfd84d 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/properties-and-attributes/properties-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import { AttributeModel, AttributesGroup, diff --git a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts index 4c53edf6bd..ce44aaff50 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/composition/tabs/relations/relations-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {ICompositionViewModelScope} from "../../composition-view-model"; import {CapabilitiesGroup, Requirement, RequirementsGroup} from "app/models"; import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts index 86bc478048..c652da4eab 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/deployment-artifacts/deployment-artifacts-view-model.ts @@ -20,6 +20,7 @@ //@require "./*.html" 'use strict'; +import * as _ from "lodash"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {ArtifactModel, ArtifactGroupModel, Resource} from "app/models"; import {ArtifactsUtils, ModalsHandler, ValidationUtils} from "app/utils"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts index 5ffae64689..eab06f28e8 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/disribution-status-modal/disribution-status-modal-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {Distribution, DistributionComponent, ExportExcel} from "app/models"; interface IDistributionStatusModalViewModelScope { diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts index 663361cd85..47ec1fd9e3 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {Distribution, DistributionComponent, Service} from "app/models"; import {ModalsHandler, Dictionary} from "app/utils"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html index 710336af15..f32e7c7fdf 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/distribution/distribution-view.html @@ -5,6 +5,7 @@ <div class="header-spacer"></div> <div class="top-search"> <input type="text" + style="width: auto;" class="search-text" data-tests-id="searchTextbox" placeholder="Search" diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts index 6060130191..b03d7c4d7c 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {ModalsHandler, ValidationUtils, EVENTS, CHANGE_COMPONENT_CSAR_VERSION_FLAG, ComponentType, DEFAULT_ICON, ResourceType, ComponentState} from "app/utils"; import {CacheService, EventListenerService, ProgressService, OnboardingService} from "app/services"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html index e3eb903a36..36976c610e 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/general/general-view.html @@ -28,7 +28,6 @@ name="componentName" data-ng-init="isCreateMode() && validateName(true)" data-ng-maxlength="50" - maxlength="50" data-ng-model="component.name" type="text" data-required @@ -117,7 +116,6 @@ data-ng-model="component.description" data-ng-model-options="{ debounce: 500 }" data-ng-pattern="validation.commentValidationPattern" - maxlength="1024" data-tests-id="description"></textarea> <!-- placeholder="Description here..." --> @@ -178,10 +176,11 @@ data-ng-pattern="validation.contactIdValidationPattern" data-ng-model-options="{ debounce: 500 }" data-tests-id="contactId" - maxlength="50" + data-ng-maxlength="50" /> <div class="input-error" data-ng-show="validateField(editForm.contactId)"> + <span ng-show="editForm.contactId.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span> <span ng-show="editForm.contactId.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_REQUIRED"></span> <span ng-show="editForm.contactId.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_CONTACT_NOT_VALID"></span> </div> @@ -196,7 +195,7 @@ data-ng-model="component.projectCode" data-ng-class="{'view-mode': isViewMode()}" data-ng-model-options="{ debounce: 500 }" - maxlength="50" + data-ng-maxlength="50" data-required name="projectCode" data-ng-pattern="validation.projectCodeValidationPattern" @@ -204,6 +203,7 @@ /> <div class="input-error" data-ng-show="validateField(editForm.projectCode)"> + <span ng-show="editForm.contactId.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '50' }"></span> <span ng-show="editForm.projectCode.$error.required" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_REQUIRED"></span> <span ng-show="editForm.projectCode.$error.pattern" translate="NEW_SERVICE_RESOURCE_ERROR_PROJECT_CODE_NOT_VALID"></span> </div> @@ -222,7 +222,6 @@ name="vendorName" data-ng-change="onVendorNameChange(oldValue)" data-ng-pattern="validation.VendorNameValidationPattern" - maxlength="60" data-ng-disabled="component.isAlreadyCertified() || (component.isCsarComponent() && component.vendorName && component.vendorName!=='')" data-tests-id="vendorName" /> @@ -250,7 +249,6 @@ data-required name="vendorRelease" data-ng-pattern="validation.VendorReleaseValidationPattern" - maxlength="25" data-ng-disabled="component.isCsarComponent() && component.vendorRelease && component.vendorRelease!==''" data-tests-id="vendorRelease" /> @@ -275,7 +273,6 @@ data-ng-maxlength="65" name="resourceVendorModelNumber" data-ng-pattern="validation.VendorModelNumberValidationPattern" - maxlength="65" data-tests-id="resourceVendorModelNumber" /> @@ -312,10 +309,9 @@ <div ng-if="component.isService()" class="i-sdc-form-item" data-ng-class="{'error': validateField(editForm.namingPolicy)}"> <label class="i-sdc-form-label">Naming policy</label> <input class="i-sdc-form-input" - name="fullName" + name="namingPolicy" data-ng-class="{'view-mode': isViewMode() || !component.ecompGeneratedNaming}" data-ng-maxlength="100" - maxlength="100" data-ng-model="component.namingPolicy" type="text" data-ng-model-options="{ debounce: 500 }" @@ -345,10 +341,9 @@ data-tests-id="serviceType" data-ng-maxlength="256" data-ng-pattern="validation.ServiceTypeAndRoleValidationPattern" - maxlength="256" /> <div class="input-error" data-ng-show="validateField(editForm.serviceType)"> - <span ng-show="editForm.serviceType.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span> + <span ng-show="editForm.serviceType.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span> <span ng-show="editForm.serviceType.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> </div> </div> @@ -367,10 +362,9 @@ data-tests-id="serviceRole" data-ng-maxlength="256" data-ng-pattern="validation.ServiceTypeAndRoleValidationPattern" - maxlength="256" /> <div class="input-error" data-ng-show="validateField(editForm.serviceRole)"> - <span ng-show="editForm.serviceRole.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '25' }"></span> + <span ng-show="editForm.serviceRole.$error.maxlength" translate="VALIDATION_ERROR_MAX_LENGTH" translate-values="{'max': '256' }"></span> <span ng-show="editForm.serviceRole.$error.pattern" translate="VALIDATION_ERROR_SPECIAL_CHARS_NOT_ALLOWED"></span> </div> </div> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts index b2fd4d68c0..47a96fb385 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/information-artifacts/information-artifacts-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {ModalsHandler} from "app/utils"; import {SharingService} from "app/services"; import {IAppConfigurtaion, ArtifactModel, IFileDownload} from "app/models"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts index 2e871a5f8d..2a7cd3dd65 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/resource-input/resource-inputs-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {ComponentInstance, InstancesInputsOrPropertiesMapData, Resource, PropertyModel, InputModel} from "app/models"; import {ModalsHandler} from "app/utils"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts index 00f3347b74..bdbc0a4334 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/inputs/service-input/service-inputs-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {ComponentInstance, InstancesInputsOrPropertiesMapData, Service, IAppMenu, InputModel, PropertyModel, InputPropertyBase} from "app/models"; import {DataTypesService} from "app/services"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts index 488e4c774d..af11e1cf9a 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/network-call-flow/network-call-flow-view-model.ts @@ -19,6 +19,7 @@ */ 'use strict'; +import * as _ from "lodash"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {VendorModel} from "app/view-models/workspace/tabs/management-workflow/management-workflow-view-model"; import {ResourceType, ArtifactType} from "app/utils"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts index b6cbf65cf0..f63ab1ccbc 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/req-and-capabilities/req-and-capabilities-view-model.ts @@ -22,6 +22,7 @@ * Created by rcohen on 9/22/2016. */ 'use strict'; +import * as _ from "lodash"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {ModalsHandler} from "app/utils"; import {Capability, PropertyModel, Requirement} from "app/models"; diff --git a/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts index 77b5ab74eb..a1f8152cea 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/tosca-artifacts/tosca-artifacts-view-model.ts @@ -19,10 +19,12 @@ */ 'use strict'; +import * as _ from "lodash"; import {ArtifactModel, IFileDownload} from "app/models"; import {IWorkspaceViewModelScope} from "app/view-models/workspace/workspace-view-model"; import {ComponentGenericResponse} from "../../../../ng2/services/responses/component-generic-response"; import {ComponentServiceNg2} from "../../../../ng2/services/component-services/component.service"; + export interface IToscaArtifactsScope extends IWorkspaceViewModelScope { artifacts:Array<ArtifactModel>; tableHeadersList:Array<any>; diff --git a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts index ca34d6fdc4..01b3aa3b22 100644 --- a/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts +++ b/catalog-ui/src/app/view-models/workspace/workspace-view-model.ts @@ -7,9 +7,9 @@ * 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. @@ -22,6 +22,7 @@ * Created by obarda on 3/30/2016. */ 'use strict'; +import * as _ from "lodash"; import {IUserProperties, IAppMenu, Resource, Component, Plugin, PluginsConfiguration, PluginDisplayOptions} from "app/models"; import { WorkspaceMode, ComponentFactory, ChangeLifecycleStateHandler, Role, ComponentState, MenuItemGroup, MenuHandler, @@ -50,6 +51,7 @@ export interface IWorkspaceViewModelScope extends ng.IScope { isNew:boolean; isFromImport:boolean; isValidForm:boolean; + isActiveTopBar:boolean; mode:WorkspaceMode; breadcrumbsModel:Array<MenuItemGroup>; sdcMenu:IAppMenu; @@ -197,6 +199,7 @@ export class WorkspaceViewModel { this.$scope.isComposition = (this.$state.current.name.indexOf(States.WORKSPACE_COMPOSITION) > -1); this.$scope.isDeployment = this.$state.current.name == States.WORKSPACE_DEPLOYMENT; this.$scope.progressService = this.progressService; + this.$scope.isActiveTopBar = true; this.$scope.getComponent = ():Component => { return this.$scope.component; @@ -267,7 +270,7 @@ export class WorkspaceViewModel { this.$scope.onVersionChanged = (selectedId:string):void => { if (this.$state.current.data && this.$state.current.data.unsavedChanges) { this.$scope.changeVersion.selectedVersion = _.find(this.$scope.versionsList, (versionObj)=> { - return versionObj.versionId === this.$scope.component.uniqueId; + return versionObj.versionId === this.$scope.component.uniqueId; }); } this.$scope.isLoading = true; @@ -323,13 +326,15 @@ export class WorkspaceViewModel { this.showSuccessNotificationMessage(); this.progressService.deleteProgressValue(this.$scope.component.uniqueId); - //update components for breadcrumbs + + // Update the components list for breadcrumbs this.components.unshift(component); + this.$state.go(States.WORKSPACE_GENERAL, { id: component.uniqueId, type: component.componentType.toLowerCase(), components: this.components - }); + }, { inherit: false }); deferred.resolve(true); }; @@ -347,17 +352,20 @@ export class WorkspaceViewModel { return item === component.name }); - // Update the components + // Update the components list for breadcrumbs + const bcIdx = this.MenuHandler.findBreadcrumbComponentIndex(this.components, component); + if (bcIdx !== -1) { + this.components[bcIdx] = component; + this.initBreadcrumbs(); // re-calculate breadcrumbs + } + + // Update the component this.$scope.component = component; this.$scope.originComponent = this.ComponentFactory.createComponent(this.$scope.component); - //update components for breadcrumbs - this.components.unshift(component); - // Enable left tags this.$scope.enabledTabs(); - if (this.$state.current.data) { this.$state.current.data.unsavedChanges = false; } @@ -469,11 +477,11 @@ export class WorkspaceViewModel { } //when checking out a minor version uuid remains - let bcComponent:Component = _.find(this.components, (item) => { + const bcIdx = _.findIndex(this.components, (item) => { return item.uuid === component.uuid; }); - if (bcComponent) { - this.components[this.components.indexOf(bcComponent)] = component; + if (bcIdx !== -1) { + this.components[bcIdx] = component; } else { //when checking out a major(certified) version this.components.unshift(component); @@ -681,7 +689,7 @@ export class WorkspaceViewModel { }); this.$scope.getTabTitle = ():string => { - return this.$scope.leftBarTabs.menuItems.find((menuItem:MenuItem)=>{ + return this.$scope.leftBarTabs.menuItems.find((menuItem:MenuItem) => { return menuItem.state == this.$scope.$state.current.name; }).text; }; @@ -690,6 +698,10 @@ export class WorkspaceViewModel { this.$state.go(this.$state.current.name,{id:component.uniqueId},{reload:true}); }; + this.$scope.$on('setWorkspaceTopBarActive', (event:ng.IAngularEvent, isActive:boolean) => { + this.$scope.isActiveTopBar = isActive; + }); + }; private initAfterScope = ():void => { @@ -718,9 +730,9 @@ export class WorkspaceViewModel { return new MenuItem(text, null, States.WORKSPACE_GENERAL, 'goToState', [this.$state.params]); }; - private updateMenuItemByRole = (menuItems:Array<any>, role:string) : Array<any> => { - let tempMenuItems:Array<any> = new Array<any>(); - menuItems.forEach((item:any) => { + private updateMenuItemByRole = (menuItems:Array<MenuItem>, role:string) => { + let tempMenuItems:Array<MenuItem> = new Array<MenuItem>(); + menuItems.forEach((item:MenuItem) => { //remove item if role is disabled if (!(item.disabledRoles && item.disabledRoles.indexOf(role) > -1)) { tempMenuItems.push(item); @@ -810,6 +822,4 @@ export class WorkspaceViewModel { }); }; -} - - +}
\ No newline at end of file diff --git a/catalog-ui/src/app/view-models/workspace/workspace-view.html b/catalog-ui/src/app/view-models/workspace/workspace-view.html index d724b8d058..0f686b1d64 100644 --- a/catalog-ui/src/app/view-models/workspace/workspace-view.html +++ b/catalog-ui/src/app/view-models/workspace/workspace-view.html @@ -1,13 +1,12 @@ <div class="sdc-workspace-container"> <loader data-display="isLoading"></loader> <div class="w-sdc-main-container"> - <div class="w-sdc-left-sidebar" data-ng-if="!isComposition"> <div class="menu-header" tooltips tooltip-content="{{menuComponentTitle}}"> {{menuComponentTitle}} </div> - <div class="i-sdc-designer-sidebar-section-content-item" ng-class="{'selected': isSelected(menuItem)}" ng-repeat="menuItem in leftBarTabs.menuItems track by $index"> - <div class="expand-collapse-menu-box-item-text" ng-click="menuItem.callback()" ng-class="{'disabled': menuItem.isDisabled }" data-tests-id="{{menuItem.text}}LeftSideMenu">{{menuItem.text}}</div> + <div class="i-sdc-designer-sidebar-section-content-item" ng-class="{'selected': menuItem.state == $state.current.name}" ng-repeat="menuItem in leftBarTabs.menuItems track by $index"> + <div class="expand-collapse-menu-box-item-text" ng-click="onMenuItemPressed(menuItem.state)" ng-class="{'disabled': menuItem.isDisabled }" data-tests-id="{{menuItem.text}}LeftSideMenu">{{menuItem.text}}</div> </div> </div> @@ -18,7 +17,7 @@ <div class="version-container"> <span data-ng-if="!isCreateMode() && !component.isLatestVersion()" class="not-latest"></span> - <select class="version-selector" data-ng-if="!isCreateMode()" data-tests-id="versionHeader" data-ng-model="changeVersion.selectedVersion" + <select class="version-selector" data-ng-if="!isCreateMode()" data-tests-id="versionHeader" data-ng-model="changeVersion.selectedVersion" data-ng-class="{'disabled': !isActiveTopBar}" ng-options="'V'+version.versionNumber for version in versionsList" data-ng-change="onVersionChanged(changeVersion.selectedVersion.versionId)"> </select> </div> @@ -35,10 +34,10 @@ <div class="sdc-workspace-top-bar-buttons"> - <span ng-if="!isCreateMode() && !component.isLatestVersion() && !showChangeStateButton()">Switch to the <a ng-click="getLatestVersion()">latest version</a></span> + <span ng-if="!isCreateMode() && !component.isLatestVersion() && !showChangeStateButton()" [disabled]="!isActiveTopBar">Switch to the <a ng-click="getLatestVersion()">latest version</a></span> <button ng-if="isDesigner() && !isCreateMode()" - data-ng-class="{'disabled' :!isValidForm || isDisableMode() || isViewMode()}" + data-ng-class="{'disabled' :!isValidForm || isDisableMode() || isViewMode() || !isActiveTopBar}" ng-click="save()" class="tlv-btn blue" data-tests-id="create/save" @@ -48,22 +47,22 @@ <button ng-repeat="(key,button) in changeLifecycleStateButtons" ng-click="changeLifecycleState(key)" ng-if="showChangeStateButton() && key != 'deleteVersion'" - data-ng-disabled="isCreateMode() || button.disabled || disabledButtons || !isValidForm" + data-ng-disabled="isCreateMode() || button.disabled || disabledButtons || !isValidForm || !isActiveTopBar" class="change-lifecycle-state-btn tlv-btn" ng-class="$first ? 'outline green' : 'grey'" data-tests-id="{{button.text | testsId}}"> {{button.text}} </button> - <button ng-if="!isViewMode() && isCreateMode()" data-ng-disabled="!isValidForm || isDisableMode() || isLoading" ng-click="save()" class="tlv-btn outline green" data-tests-id="create/save">Create</button> + <button ng-if="!isViewMode() && isCreateMode()" data-ng-disabled="!isValidForm || isDisableMode() || isLoading || !isActiveTopBar" ng-click="save()" class="tlv-btn outline green" data-tests-id="create/save">Create</button> <span data-ng-if="isDesigner() && !isCreateMode() && component.lifecycleState === 'NOT_CERTIFIED_CHECKOUT'" sdc-smart-tooltip="" - data-ng-class="{'disabled' : !isValidForm || isDisableMode() || isViewMode()}" ng-click="changeLifecycleState('deleteVersion')" + data-ng-class="{'disabled' : !isValidForm || isDisableMode() || isViewMode() || !isActiveTopBar}" ng-click="changeLifecycleState('deleteVersion')" class="sprite-new delete-btn" data-tests-id="delete_version" sdc-smart-tooltip="">Delete</span> - <span data-ng-if="isDesigner()" data-ng-class="{'disabled' :isDisableMode() || isViewMode()}" ng-click="revert()" class="sprite-new revert-btn" data-tests-id="revert" + <span data-ng-if="isDesigner()" data-ng-class="{'disabled' :isDisableMode() || isViewMode() || !isActiveTopBar}" ng-click="revert()" class="sprite-new revert-btn" data-tests-id="revert" data-ng-show="showFullIcons()" sdc-smart-tooltip="">Revert</span> - + <span class="delimiter"></span> <span class="sprite-new x-btn" data-ng-click="goToBreadcrumbHome()" sdc-smart-tooltip="">Close</span> diff --git a/catalog-ui/src/assets/languages/en_US.json b/catalog-ui/src/assets/languages/en_US.json index f824f90a41..70018afab6 100644 --- a/catalog-ui/src/assets/languages/en_US.json +++ b/catalog-ui/src/assets/languages/en_US.json @@ -441,6 +441,10 @@ "MAX_VALIDATION_ERROR": "Value cannot be higher than the 'max_vf_module_instances' and 'initial_count'", "MIN_MAX_VALIDATION": "Value must be between 'min_vf_module_instances' and 'max vf module instances'", "MIN_VALIDATION_VF_LEVE_ERROR": " Value of 'min_vf_module_instances' must not be lower than defined in VF level", - "MAX_VALIDATION_VF_LEVE_ERROR": " Value of 'max_vf_module_instances' must not be higher than defined in VF level" + "MAX_VALIDATION_VF_LEVE_ERROR": " Value of 'max_vf_module_instances' must not be higher than defined in VF level", + + "=========== SERVICE PATH SELECTOR ===========": "", + "SERVICE_PATH_SELECTOR_HIDE_ALL_VALUE" : "⚊ Hide all ⚊", + "SERVICE_PATH_SELECTOR_SHOW_ALL_VALUE" : "⚊ Show all ⚊" } diff --git a/catalog-ui/src/assets/styles/app.less b/catalog-ui/src/assets/styles/app.less index 8042c0e7b7..f48fd864ef 100644 --- a/catalog-ui/src/assets/styles/app.less +++ b/catalog-ui/src/assets/styles/app.less @@ -14,6 +14,8 @@ @import 'sprite.less'; @import 'sprite-resource-icons.less'; @import 'sprite-services-icons.less'; +@import 'sprite-group-icons.less'; +@import 'sprite-policy-icons.less'; @import 'animation.less'; @import 'buttons.less'; @@ -35,6 +37,7 @@ @import 'welcome-sprite.less'; @import 'welcome-style.less'; @import 'notification-template.less'; +@import 'tables.less'; // Less insides specific files. @import '../../app/directives/ecomp-header/ecomp-header.less'; diff --git a/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding.svg b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding.svg new file mode 100644 index 0000000000..1a9a507948 --- /dev/null +++ b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding.svg @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="44px" height="44px" viewBox="0 0 44 44" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch --> + <title>Group 12</title> + <desc>Created with Sketch.</desc> + <defs> + <filter x="-79.5%" y="-65.9%" width="259.1%" height="259.1%" filterUnits="objectBoundingBox" id="filter-1"> + <feOffset dx="0" dy="3" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"></feMergeNode> + <feMergeNode in="SourceGraphic"></feMergeNode> + </feMerge> + </filter> + <linearGradient x1="0%" y1="0%" x2="104.899315%" y2="128.265625%" id="linearGradient-2"> + <stop stop-color="#009E98" offset="0%"></stop> + <stop stop-color="#97D648" offset="100%"></stop> + </linearGradient> + <path d="M3,0 L19,0 C20.6568542,-3.04359188e-16 22,1.34314575 22,3 L22,19 C22,20.6568542 20.6568542,22 19,22 L3,22 C1.34314575,22 2.02906125e-16,20.6568542 0,19 L0,3 C-2.02906125e-16,1.34314575 1.34314575,3.04359188e-16 3,0 Z" id="path-3"></path> + <filter x="-11.4%" y="-11.4%" width="122.7%" height="122.7%" filterUnits="objectBoundingBox" id="filter-4"> + <feGaussianBlur stdDeviation="1.5" in="SourceAlpha" result="shadowBlurInner1"></feGaussianBlur> + <feOffset dx="0" dy="0" in="shadowBlurInner1" result="shadowOffsetInner1"></feOffset> + <feComposite in="shadowOffsetInner1" in2="SourceAlpha" operator="arithmetic" k2="-1" k3="1" result="shadowInnerInner1"></feComposite> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0.478431373 0 0 0 0 0.243137255 0 0 0 1 0" type="matrix" in="shadowInnerInner1"></feColorMatrix> + </filter> + <path d="M6,4 L6,1 C6,0.4 5.6,0 5,0 C4.4,0 4,0.4 4,1 L4,4 L1,4 C0.4,4 0,4.4 0,5 C0,5.6 0.4,6 1,6 L4,6 L4,9 C4,9.6 4.4,10 5,10 C5.6,10 6,9.6 6,9 L6,6 L9,6 C9.6,6 10,5.6 10,5 C10,4.4 9.6,4 9,4 L6,4 Z" id="path-5"></path> + </defs> + <g id="sdc" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Group-12" transform="translate(11.000000, 8.000000)"> + <g id="Group-4-Copy"> + <g id="Group-3" filter="url(#filter-1)"> + <g id="Rectangle-15-Copy-5"> + <use fill="url(#linearGradient-2)" fill-rule="evenodd" xlink:href="#path-3"></use> + <use fill="black" fill-opacity="1" filter="url(#filter-4)" xlink:href="#path-3"></use> + <use stroke="#FFFFFF" stroke-width="2" xlink:href="#path-3"></use> + </g> + </g> + </g> + <g id="color-/-gray-#5A5A5A-/-idle--Copy-34" transform="translate(6.000000, 6.000000)"> + <mask id="mask-6" fill="white"> + <use xlink:href="#path-5"></use> + </mask> + <use id="Mask" fill="#FFFFFF" xlink:href="#path-5"></use> + </g> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding_group.svg b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding_group.svg new file mode 100644 index 0000000000..90920e9001 --- /dev/null +++ b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding_group.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch --> + <title>Group 12 Copy</title> + <desc>Created with Sketch.</desc> + <defs> + <filter x="-22.7%" y="-13.6%" width="145.5%" height="145.5%" filterUnits="objectBoundingBox" id="filter-1"> + <feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"></feMergeNode> + <feMergeNode in="SourceGraphic"></feMergeNode> + </feMerge> + </filter> + <path d="M6,4 L6,1 C6,0.4 5.6,0 5,0 C4.4,0 4,0.4 4,1 L4,4 L1,4 C0.4,4 0,4.4 0,5 C0,5.6 0.4,6 1,6 L4,6 L4,9 C4,9.6 4.4,10 5,10 C5.6,10 6,9.6 6,9 L6,6 L9,6 C9.6,6 10,5.6 10,5 C10,4.4 9.6,4 9,4 L6,4 Z" id="path-2"></path> + </defs> + <g id="sdc" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Group-12-Copy" transform="translate(3.000000, 1.000000)"> + <g id="Group-4-Copy" filter="url(#filter-1)" fill="#009FDB" stroke="#FFFFFF"> + <g id="Group-3"> + <path d="M3,0 L19,0 C20.6568542,-3.04359188e-16 22,1.34314575 22,3 L22,19 C22,20.6568542 20.6568542,22 19,22 L3,22 C1.34314575,22 2.02906125e-16,20.6568542 0,19 L0,3 C-2.02906125e-16,1.34314575 1.34314575,3.04359188e-16 3,0 Z" id="Rectangle-15-Copy-5"></path> + </g> + </g> + <g id="color-/-gray-#5A5A5A-/-idle--Copy-34" transform="translate(6.000000, 6.000000)"> + <mask id="mask-3" fill="white"> + <use xlink:href="#path-2"></use> + </mask> + <use id="Mask" fill="#FFFFFF" xlink:href="#path-2"></use> + </g> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding_policy.svg b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding_policy.svg new file mode 100644 index 0000000000..217d92c654 --- /dev/null +++ b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/adding_policy.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch --> + <title>Group 12 Copy</title> + <desc>Created with Sketch.</desc> + <defs> + <filter x="-22.7%" y="-13.6%" width="145.5%" height="145.5%" filterUnits="objectBoundingBox" id="filter-1"> + <feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix> + <feMerge> + <feMergeNode in="shadowMatrixOuter1"></feMergeNode> + <feMergeNode in="SourceGraphic"></feMergeNode> + </feMerge> + </filter> + <path d="M6,4 L6,1 C6,0.4 5.6,0 5,0 C4.4,0 4,0.4 4,1 L4,4 L1,4 C0.4,4 0,4.4 0,5 C0,5.6 0.4,6 1,6 L4,6 L4,9 C4,9.6 4.4,10 5,10 C5.6,10 6,9.6 6,9 L6,6 L9,6 C9.6,6 10,5.6 10,5 C10,4.4 9.6,4 9,4 L6,4 Z" id="path-2"></path> + </defs> + <g id="sdc" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Group-12-Copy" transform="translate(3.000000, 1.000000)"> + <g id="Group-4-Copy" filter="url(#filter-1)" fill="#0568AE" stroke="#FFFFFF"> + <g id="Group-3"> + <path d="M3,0 L19,0 C20.6568542,-3.04359188e-16 22,1.34314575 22,3 L22,19 C22,20.6568542 20.6568542,22 19,22 L3,22 C1.34314575,22 2.02906125e-16,20.6568542 0,19 L0,3 C-2.02906125e-16,1.34314575 1.34314575,3.04359188e-16 3,0 Z" id="Rectangle-15-Copy-5"></path> + </g> + </g> + <g id="color-/-gray-#5A5A5A-/-idle--Copy-34" transform="translate(6.000000, 6.000000)"> + <mask id="mask-3" fill="white"> + <use xlink:href="#path-2"></use> + </mask> + <use id="Mask" fill="#FFFFFF" xlink:href="#path-2"></use> + </g> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/catalog-ui/src/assets/styles/images/canvas-tagging-icons/group_added.svg b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/group_added.svg new file mode 100644 index 0000000000..d88e7ca343 --- /dev/null +++ b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/group_added.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch --> + <title>Group 13</title> + <desc>Created with Sketch.</desc> + <defs> + <path d="M3,0 L15,0 C16.6568542,-3.04359188e-16 18,1.34314575 18,3 L18,15 C18,16.6568542 16.6568542,18 15,18 L3,18 C1.34314575,18 -6.85272294e-16,16.6568542 -8.8817842e-16,15 L0,3 C-2.02906125e-16,1.34314575 1.34314575,-5.83819232e-16 3,-8.8817842e-16 Z" id="path-1"></path> + <filter x="-25.0%" y="-13.9%" width="150.0%" height="150.0%" filterUnits="objectBoundingBox" id="filter-2"> + <feMorphology radius="0.5" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology> + <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix> + </filter> + <path d="M12.6603086,4.57405469 L8.30209375,11.5833406 L5.57568359,9.20957266 C5.13068359,8.77957266 4.46368359,8.77957266 4.01968359,9.20957266 C3.57568359,9.64057266 3.57568359,10.2865727 4.01968359,10.7175727 L7.52409375,13.8453406 C7.74609375,14.0603406 7.96809375,14.1683406 8.30209375,14.1683406 C8.63509375,14.1683406 8.85809375,14.0603406 9.07909375,13.8453406 L14.2163086,6.08105469 C14.6603086,5.65105469 14.6603086,5.00505469 14.2163086,4.57405469 C13.7723086,4.14305469 13.1053086,4.14305469 12.6603086,4.57405469 Z" id="path-3"></path> + </defs> + <g id="sdc" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Group-13" transform="translate(3.000000, 1.000000)"> + <g id="Group-4-Copy-9"> + <g id="Group-3"> + <g id="Rectangle-15-Copy-5"> + <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use> + <use stroke="#FFFFFF" stroke-width="1" fill="#009FDB" fill-rule="evenodd" xlink:href="#path-1"></use> + </g> + </g> + </g> + <mask id="mask-4" fill="white"> + <use xlink:href="#path-3"></use> + </mask> + <use id="Mask" fill="#FFFFFF" xlink:href="#path-3"></use> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/catalog-ui/src/assets/styles/images/canvas-tagging-icons/indication.svg b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/indication.svg new file mode 100644 index 0000000000..7581d0447e --- /dev/null +++ b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/indication.svg @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch --> + <title>Group 4 Copy 10</title> + <desc>Created with Sketch.</desc> + <defs> + <path d="M3,0 L15,0 C16.6568542,-3.04359188e-16 18,1.34314575 18,3 L18,15 C18,16.6568542 16.6568542,18 15,18 L3,18 C1.34314575,18 -6.85272294e-16,16.6568542 -8.8817842e-16,15 L0,3 C-2.02906125e-16,1.34314575 1.34314575,-5.83819232e-16 3,-8.8817842e-16 Z" id="path-1"></path> + <filter x="-41.7%" y="-30.6%" width="183.3%" height="183.3%" filterUnits="objectBoundingBox" id="filter-2"> + <feMorphology radius="0.5" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology> + <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix> + </filter> + </defs> + <g id="sdc" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Group-4-Copy-10" transform="translate(5.000000, 3.000000)"> + <g id="Group-3"> + <g id="Rectangle-15-Copy-5"> + <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use> + <use stroke="#FFFFFF" stroke-width="1" fill-opacity="0.3" fill="#009FDB" fill-rule="evenodd" xlink:href="#path-1"></use> + </g> + </g> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/catalog-ui/src/assets/styles/images/canvas-tagging-icons/policy_added.svg b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/policy_added.svg new file mode 100644 index 0000000000..9ff39019c7 --- /dev/null +++ b/catalog-ui/src/assets/styles/images/canvas-tagging-icons/policy_added.svg @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch --> + <title>Group 13 Copy</title> + <desc>Created with Sketch.</desc> + <defs> + <path d="M3,0 L15,0 C16.6568542,-3.04359188e-16 18,1.34314575 18,3 L18,15 C18,16.6568542 16.6568542,18 15,18 L3,18 C1.34314575,18 -6.85272294e-16,16.6568542 -8.8817842e-16,15 L0,3 C-2.02906125e-16,1.34314575 1.34314575,-5.83819232e-16 3,-8.8817842e-16 Z" id="path-1"></path> + <filter x="-25.0%" y="-13.9%" width="150.0%" height="150.0%" filterUnits="objectBoundingBox" id="filter-2"> + <feMorphology radius="0.5" operator="dilate" in="SourceAlpha" result="shadowSpreadOuter1"></feMorphology> + <feOffset dx="0" dy="2" in="shadowSpreadOuter1" result="shadowOffsetOuter1"></feOffset> + <feGaussianBlur stdDeviation="1" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur> + <feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"></feComposite> + <feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix> + </filter> + <path d="M12.6603086,4.57405469 L8.30209375,11.5833406 L5.57568359,9.20957266 C5.13068359,8.77957266 4.46368359,8.77957266 4.01968359,9.20957266 C3.57568359,9.64057266 3.57568359,10.2865727 4.01968359,10.7175727 L7.52409375,13.8453406 C7.74609375,14.0603406 7.96809375,14.1683406 8.30209375,14.1683406 C8.63509375,14.1683406 8.85809375,14.0603406 9.07909375,13.8453406 L14.2163086,6.08105469 C14.6603086,5.65105469 14.6603086,5.00505469 14.2163086,4.57405469 C13.7723086,4.14305469 13.1053086,4.14305469 12.6603086,4.57405469 Z" id="path-3"></path> + </defs> + <g id="sdc" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="Group-13-Copy" transform="translate(3.000000, 1.000000)"> + <g id="Group-4-Copy-9"> + <g id="Group-3"> + <g id="Rectangle-15-Copy-5"> + <use fill="black" fill-opacity="1" filter="url(#filter-2)" xlink:href="#path-1"></use> + <use stroke="#FFFFFF" stroke-width="1" fill="#0568AE" fill-rule="evenodd" xlink:href="#path-1"></use> + </g> + </g> + </g> + <mask id="mask-4" fill="white"> + <use xlink:href="#path-3"></use> + </mask> + <use id="Mask" fill="#FFFFFF" xlink:href="#path-3"></use> + </g> + </g> +</svg>
\ No newline at end of file diff --git a/catalog-ui/src/assets/styles/images/resource-icons/monitoring_template.png b/catalog-ui/src/assets/styles/images/resource-icons/monitoring_template.png Binary files differnew file mode 100644 index 0000000000..f5b73989a6 --- /dev/null +++ b/catalog-ui/src/assets/styles/images/resource-icons/monitoring_template.png diff --git a/catalog-ui/src/assets/styles/images/sprites/sprite-policy-groups-icons.png b/catalog-ui/src/assets/styles/images/sprites/sprite-policy-groups-icons.png Binary files differnew file mode 100644 index 0000000000..fe1723e2fa --- /dev/null +++ b/catalog-ui/src/assets/styles/images/sprites/sprite-policy-groups-icons.png diff --git a/catalog-ui/src/assets/styles/sprite-group-icons.less b/catalog-ui/src/assets/styles/sprite-group-icons.less new file mode 100644 index 0000000000..47340adbb3 --- /dev/null +++ b/catalog-ui/src/assets/styles/sprite-group-icons.less @@ -0,0 +1,11 @@ +.sprite-group-icons { + background-image: url('images/sprites/sprite-policy-groups-icons.png'); + display: inline-block; +} + +.sprite-group-icons.disable { opacity:0.5;} +.sprite-group-icons.group { background-position: -43px -42px; width: 40px; height: 40px;} +.sprite-group-icons.group-border { background-position: -123px -36px; width: 55px; height: 55px;} + +sprite-group-icons.group-empty { background-position: -40px -274px; width: 47px; height: 47px;} +sprite-group-icons.group-empty-border { background-position: -123px -270px; width: 55px; height: 55px;}
\ No newline at end of file diff --git a/catalog-ui/src/assets/styles/sprite-policy-icons.less b/catalog-ui/src/assets/styles/sprite-policy-icons.less new file mode 100644 index 0000000000..154cf0b6a3 --- /dev/null +++ b/catalog-ui/src/assets/styles/sprite-policy-icons.less @@ -0,0 +1,12 @@ +.sprite-policy-icons { + background-image: url('images/sprites/sprite-policy-groups-icons.png'); + display: inline-block; +} + +.sprite-policy-icons.disable { opacity:0.5;} + +.sprite-policy-icons.policy { background-position: -42px -130px; width: 40px; height: 40px;} +.sprite-policy-icons.policy-border { background-position: -123px -123px; width: 55px; height: 55px;} + +.sprite-policy-icons.policy-empty { background-position: -40px -546px; width: 47px; height: 47px;} +.sprite-policy-icons.policy-empty-border { background-position: -123px -542px; width: 55px; height: 55px;} diff --git a/catalog-ui/src/assets/styles/sprite-resource-icons.less b/catalog-ui/src/assets/styles/sprite-resource-icons.less index 96993d5171..2f542d661c 100644 --- a/catalog-ui/src/assets/styles/sprite-resource-icons.less +++ b/catalog-ui/src/assets/styles/sprite-resource-icons.less @@ -195,10 +195,10 @@ .sprite-resource-icons.ucpe.medium { background-position: -141px -3137px; width: 41px; height: 41px;} .sprite-resource-icons.ucpe.large { background-position: -70px -3119px; width: 60px; height: 60px;} -.sprite-resource-icons.vrouter { background-position: -282px -3066px; width: 69px; height: 43px;} -.sprite-resource-icons.vrouter.small { background-position: -210px -3080px; width: 29px; height: 29px;} -.sprite-resource-icons.vrouter.medium { background-position: -141px -3068px; width: 41px; height: 41px;} -.sprite-resource-icons.vrouter.large { background-position: -70px -3049px; width: 60px; height: 60px;} +.sprite-resource-icons.vRouter { background-position: -282px -3066px; width: 69px; height: 43px;} +.sprite-resource-icons.vRouter.small { background-position: -210px -3080px; width: 29px; height: 29px;} +.sprite-resource-icons.vRouter.medium { background-position: -141px -3068px; width: 41px; height: 41px;} +.sprite-resource-icons.vRouter.large { background-position: -70px -3049px; width: 60px; height: 60px;} .sprite-resource-icons.wanx { background-position: -283px -2992px; width: 58px; height: 47px;} .sprite-resource-icons.wanx.small { background-position: -210px -3009px; width: 30px; height: 30px;} diff --git a/catalog-ui/src/assets/styles/sprite.less b/catalog-ui/src/assets/styles/sprite.less index 107cb815ee..aaf4f5529a 100644 --- a/catalog-ui/src/assets/styles/sprite.less +++ b/catalog-ui/src/assets/styles/sprite.less @@ -231,8 +231,8 @@ .asc{background-position: -924px -71px; width: 8px; height: 12px;} .desc{background-position: -924px -101px; width: 8px; height: 12px;} -.delete-item-icon { background-position: -140px -1119px; width: 11px; height: 15px; } -.delete-item-icon:hover { background-position: -167px -1119px; width: 11px; height: 15px; } +.delete-item-icon { background-position: -140px -1118px; width: 11px; height: 15px; } +.delete-item-icon:hover { background-position: -167px -1118px; width: 11px; height: 15px; } .filter-icon { background-position: -48px -1040px; width: 19px; height: 20px;} .filter-icon:hover { background-position: -99px -1040px; width: 19px; height: 20px;} .filled-checkbox-icon { background-position: -100px -1079px; width: 14px; height: 14px;} diff --git a/catalog-ui/src/assets/styles/tables.less b/catalog-ui/src/assets/styles/tables.less new file mode 100644 index 0000000000..6bac2588a4 --- /dev/null +++ b/catalog-ui/src/assets/styles/tables.less @@ -0,0 +1,54 @@ +.generic-table { + border: solid 1px @main_color_o; + color: @main_color_m; + background-color: @func_color_r; + font-size: 13px; + font-family: @font-opensans-regular; + overflow-y: auto; + + .cell { + flex: 1; + padding: 10px; + border-right: @main_color_o solid 1px; + &:last-child { + margin: 0; + flex: 0.1; + border: none; + } + } + + .header-row { + display: flex; + flex-basis: 100%; + background-color: @func_color_r; + color: @func_color_s; + font-weight: bold; + border-bottom: @main_color_o solid 1px; + position: sticky; + top: 0; + z-index: 1; + .header-cell { + padding: 10px; + } + } + + .data-row { + display: flex; + flex-basis: 100%; + border-bottom: @main_color_o solid 1px; + background-color: @main_color_p; + &:last-child { + border-bottom: none; + } + } + + .no-row-text { + color: @main_color_m; + display: flex; + flex-basis: 100%; + background-color: @main_color_p; + padding: 20px; + justify-content: center; + font-size: 14px; + } +}
\ No newline at end of file |