From 451a3400b76511393c62a444f588a4ed15f4a549 Mon Sep 17 00:00:00 2001 From: Michael Lando Date: Sun, 19 Feb 2017 10:28:42 +0200 Subject: Initial OpenECOMP SDC commit Change-Id: I0924d5a6ae9cdc161ae17c68d3689a30d10f407b Signed-off-by: Michael Lando --- .../utils/composition-graph-links-utils.ts | 347 +++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts (limited to 'catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts') diff --git a/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts new file mode 100644 index 0000000000..602e6b6def --- /dev/null +++ b/catalog-ui/app/scripts/directives/graphs-v2/composition-graph/utils/composition-graph-links-utils.ts @@ -0,0 +1,347 @@ +/** + * Created by obarda on 6/28/2016. + */ +/// +module Sdc.Graph.Utils { + + import ImageCreatorService = Sdc.Utils.ImageCreatorService; + import Module = Sdc.Models.Module; + export class CompositionGraphLinkUtils { + + private p2pVL:Models.Components.Component; + private mp2mpVL:Models.Components.Component; + + constructor(private linksFactory:Sdc.Utils.LinksFactory, + private loaderService:Services.LoaderService, + private generalGraphUtils:Sdc.Graph.Utils.CompositionGraphGeneralUtils, + private leftPaletteLoaderService:Services.Components.LeftPaletteLoaderService, + private componentInstanceFactory:Sdc.Utils.ComponentInstanceFactory, + private nodesFactory:Sdc.Utils.NodesFactory, + private commonGraphUtils: Sdc.Graph.Utils.CommonGraphUtils, + private matchCapabilitiesRequirementsUtils: Graph.Utils.MatchCapabilitiesRequirementsUtils) { + + this.initScopeVls(); + + } + + + /** + * Delete the link on server and then remove it from graph + * @param component + * @param releaseLoading - true/false release the loader when finished + * @param link - the link to delete + */ + public deleteLink = (cy:Cy.Instance, component:Models.Components.Component, releaseLoading:boolean, link:Cy.CollectionEdges) => { + + this.loaderService.showLoader('composition-graph'); + let onSuccessDeleteRelation = (response) => { + cy.remove(link); + }; + + if (!releaseLoading) { + this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIAction( + () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation) + ); + } else { + this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback( + () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation), + () => this.loaderService.hideLoader('composition-graph')); + } + }; + + /** + * create the link on server and than draw it on graph + * @param link - the link to create + * @param cy + * @param component + */ + public createLink = (link:Models.CompositionCiLinkBase, cy:Cy.Instance, component:Models.Components.Component):void => { + + this.loaderService.showLoader('composition-graph'); + + let onSuccess:(response:Models.RelationshipModel) => void = (relation:Models.RelationshipModel) => { + link.setRelation(relation); + this.commonGraphUtils.insertLinkToGraph(cy, link); + }; + + link.updateLinkDirection(); + + this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback( + () => component.createRelation(link.relation).then(onSuccess), + () => this.loaderService.hideLoader('composition-graph') + ); + }; + + + public initScopeVls = ():void => { + + let vls = this.leftPaletteLoaderService.getFullDataComponentList(Sdc.Utils.Constants.ResourceType.VL); + vls.forEach((item) => { + let key = _.find(Object.keys(item.capabilities), (key) => { + return _.includes(key.toLowerCase(), 'linkable'); + }); + let linkable = item.capabilities[key]; + if (linkable) { + if (linkable[0].maxOccurrences == '2') { + this.p2pVL = _.find(vls, (component:Models.Components.Component) => { + return component.uniqueId === item.uniqueId; + }); + + } else {//assuming unbounded occurrences + this.mp2mpVL = _.find(vls, (component:Models.Components.Component) => { + return component.uniqueId === item.uniqueId; + }); + } + } + }); + }; + + private setVLlinks = (match:Models.MatchReqToReq, vl:Models.ComponentsInstances.ComponentInstance):Array => { + + let relationship1 = new Models.Relationship(); + let relationship2 = new Models.Relationship(); + let newRelationshipModel1 = new Models.RelationshipModel(); + let newRelationshipModel2 = new Models.RelationshipModel(); + + let capability:Models.Capability = vl.capabilities.findValueByKey('linkable')[0]; + relationship1.setRelationProperties(capability, match.requirement); + relationship2.setRelationProperties(capability, match.secondRequirement); + + newRelationshipModel1.setRelationshipModelParams(match.fromNode, vl.uniqueId, [relationship1]); + newRelationshipModel2.setRelationshipModelParams(match.toNode, vl.uniqueId, [relationship2]); + + return [newRelationshipModel1, newRelationshipModel2]; + }; + + private createVlinks = (cy:Cy.Instance, component:Models.Components.Component, matchReqToReq:Models.MatchReqToReq, vl:Models.Components.Component):void => { + + let componentInstance:Models.ComponentsInstances.ComponentInstance = this.componentInstanceFactory.createComponentInstanceFromComponent(vl); + let fromNodePosition:Cy.Position = cy.getElementById(matchReqToReq.fromNode).relativePosition(); + let toNodePosition:Cy.Position = cy.getElementById(matchReqToReq.toNode).relativePosition(); + let location:Cy.Position = { + x: 0.5 * (fromNodePosition.x + toNodePosition.x), + y: 0.5 * (fromNodePosition.y + toNodePosition.y) + } + + componentInstance.posX = location.x; + componentInstance.posY = location.y; + + let onFailed:(error:any) => void = (error:any) => { + this.loaderService.hideLoader('composition-graph'); + console.info('onFailed', error); + }; + + let onSuccess = (response:Models.ComponentsInstances.ComponentInstance):void => { + + console.info('onSuccses', response); + response.requirements = new Models.RequirementsGroup(vl.requirements); + response.capabilities = new Models.CapabilitiesGroup(vl.capabilities); + response.componentVersion = vl.version; + response.setInstanceRC(); + + let newLinks = this.setVLlinks(matchReqToReq, response); + let newNode = this.nodesFactory.createNode(response); + + this.commonGraphUtils.addComponentInstanceNodeToGraph(cy, newNode); + + _.forEach(newLinks, (link) => { + let linkObg:Models.CompositionCiLinkBase = this.linksFactory.createGraphLink(cy, link, link.relationships[0]); + this.createLink(linkObg, cy, component); + }); + }; + component.createComponentInstance(componentInstance).then(onSuccess, onFailed); + }; + + private createSimpleLink = (match:Models.MatchReqToCapability, cy:Cy.Instance, component:Models.Components.Component):void => { + let newRelation:Models.RelationshipModel = match.matchToRelationModel(); + let linkObg:Models.CompositionCiLinkBase = this.linksFactory.createGraphLink(cy,newRelation, newRelation.relationships[0]); + this.createLink(linkObg, cy, component); + }; + + public createLinkFromMenu = (cy:Cy.Instance, chosenMatch:Models.MatchBase, vl:Models.Components.Component, component:Models.Components.Component):void => { + + if (chosenMatch) { + if (chosenMatch && chosenMatch instanceof Models.MatchReqToReq) { + this.createVlinks(cy, component, chosenMatch, vl); //TODO orit implement + } + if (chosenMatch && chosenMatch instanceof Models.MatchReqToCapability) { + this.createSimpleLink(chosenMatch, cy, component); + } + } + }; + + + /** + * Filters the matches for UCPE links so that shown requirements and capabilites are only related to the selected ucpe-cp + * @param fromNode + * @param toNode + * @param matchesArray + * @returns {Array} + */ + public filterUcpeLinks(fromNode: Models.Graph.CompositionCiNodeBase, toNode: Models.Graph.CompositionCiNodeBase, matchesArray: Array): any { + + let matchLink: Array; + + if (fromNode.isUcpePart) { + matchLink = _.filter(matchesArray, (match: Models.MatchBase) => { + return match.isOwner(fromNode.id); + }); + } + + if (toNode.isUcpePart) { + matchLink = _.filter(matchesArray, (match: Models.MatchBase) => { + return match.isOwner(toNode.id); + }); + } + return matchLink ? matchLink : matchesArray; + } + + + /** + * open the connect link menu if the link drawn is valid - match requirements & capabilities + * @param cy + * @param fromNode + * @param toNode + * @returns {any} + */ + public onLinkDrawn(cy:Cy.Instance, fromNode:Cy.CollectionFirstNode, toNode:Cy.CollectionFirstNode):Models.RelationMenuDirectiveObj { + + if(!this.commonGraphUtils.nodeLocationsCompatible(cy, fromNode, toNode)){ return null; } + let linkModel:Array = this.generalGraphUtils.getAllCompositionCiLinks(cy); + + let possibleRelations:Array = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromNode.data().componentInstance, + toNode.data().componentInstance, linkModel, this.mp2mpVL); //TODO orit - add p2p and mp2mp + + //filter relations found to limit to specific ucpe-cp + possibleRelations = this.filterUcpeLinks(fromNode.data(), toNode.data(), possibleRelations); + + //if found possibleRelations between the nodes we create relation menu directive and open the link menu + if (possibleRelations.length) { + let menuPosition = this.generalGraphUtils.getLinkMenuPosition(cy, toNode.renderedPoint()); + return new Models.RelationMenuDirectiveObj(fromNode.data(), toNode.data(), this.mp2mpVL, this.p2pVL, menuPosition, possibleRelations); + } + return null; + }; + + + /** + * when we drag instance in to UCPE or out of UCPE - get all links we need to delete - one node in ucpe and one node outside of ucpe + * @param node - the node we dragged into or out of the ucpe + */ + public deleteLinksWhenNodeMovedFromOrToUCPE(component:Models.Components.Component, cy:Cy.Instance, nodeMoved:Cy.CollectionNodes, vlsPendingDeletion?:Cy.CollectionNodes):void { + + + let linksToDelete:Cy.CollectionElements = cy.collection(); + _.forEach(nodeMoved.neighborhood('node'), (neighborNode)=>{ + + if(neighborNode.data().isUcpePart){ //existing connections to ucpe or ucpe-cp - we want to delete even though nodeLocationsCompatible will technically return true + linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode)); // This will delete the ucpe-host-link, or the vl-ucpe-link if nodeMoved is vl + } else if(!this.commonGraphUtils.nodeLocationsCompatible(cy, nodeMoved, neighborNode)){ //connection to regular node or vl - check if locations are compatible + if(!vlsPendingDeletion || !vlsPendingDeletion.intersect(neighborNode).length){ //Check if this is a link to a VL pending deletion, to prevent double deletion of between the node moved and vl + linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode)); + } + } + }); + + + + linksToDelete.each((i, link)=>{ + this.deleteLink(cy, component, false, link); + }); + + }; + + + /** + * Creates a hostedOn link between a VF and UCPE + * @param component + * @param cy + * @param ucpeNode + * @param vfNode + */ + public createVfToUcpeLink = (component: Models.Components.Component, cy:Cy.Instance, ucpeNode:Models.Graph.NodeUcpe, vfNode:Models.Graph.CompositionCiNodeVf):void => { + let hostedOnMatch:Models.MatchReqToCapability = this.generalGraphUtils.canBeHostedOn(cy, ucpeNode.componentInstance, vfNode.componentInstance); + /* create relation */ + let newRelation = new Models.RelationshipModel(); + newRelation.fromNode = ucpeNode.id; + newRelation.toNode = vfNode.id; + + let link:Models.CompositionCiLinkBase = this.linksFactory.createUcpeHostLink(newRelation); + link.relation = hostedOnMatch.matchToRelationModel(); + this.createLink(link, cy, component); + }; + + + /** + * Handles click event on links. + * If one edge selected: do nothing. + /*Two edges selected - always select all + /* Three or more edges: first click - select all, secondary click - select single. + * @param cy + * @param event + */ + public handleLinkClick(cy:Cy.Instance, event : Cy.EventObject) { + if(cy.$('edge:selected').length > 2 && 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); + } + } + + } + + + /** + * Calculates the position for the menu that modifies an existing link + * @param event + * @param elementWidth + * @param elementHeight + * @returns {Sdc.Models.Graph.Point} + */ + public calculateLinkMenuPosition(event, elementWidth, elementHeight): Sdc.Models.Graph.Point { + let point: Sdc.Models.Graph.Point = new Sdc.Models.Graph.Point(event.originalEvent.x,event.originalEvent.y); + if(event.originalEvent.view.screen.height-elementHeight