2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
22 * Created by obarda on 6/28/2016.
24 import * as _ from "lodash";
25 import {GraphUIObjects} from "app/utils";
26 import {LoaderService} from "app/services";
31 CompositionCiNodeBase,
40 import {CommonGraphUtils} from "../../common/common-graph-utils";
41 import {CompositionGraphGeneralUtils} from "./composition-graph-general-utils";
42 import {MatchCapabilitiesRequirementsUtils} from "./match-capability-requierment-utils";
43 import {CompositionCiServicePathLink} from "../../../../models/graph/graph-links/composition-graph-links/composition-ci-service-path-link";
45 export class CompositionGraphLinkUtils {
47 constructor(private linksFactory:LinksFactory,
48 private loaderService:LoaderService,
49 private generalGraphUtils:CompositionGraphGeneralUtils,
50 private commonGraphUtils:CommonGraphUtils,
51 private matchCapabilitiesRequirementsUtils:MatchCapabilitiesRequirementsUtils) {
56 * Delete the link on server and then remove it from graph
58 * @param releaseLoading - true/false release the loader when finished
59 * @param link - the link to delete
61 public deleteLink = (cy:Cy.Instance, component:Component, releaseLoading:boolean, link:Cy.CollectionEdges) => {
63 this.loaderService.showLoader('composition-graph');
64 let onSuccessDeleteRelation = (response) => {
68 if (!releaseLoading) {
69 this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
70 () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation)
73 this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
74 () => component.deleteRelation(link.data().relation).then(onSuccessDeleteRelation),
75 () => this.loaderService.hideLoader('composition-graph'));
80 * create the link on server and than draw it on graph
81 * @param link - the link to create
85 public createLink = (link:CompositionCiLinkBase, cy:Cy.Instance, component:Component):void => {
87 this.loaderService.showLoader('composition-graph');
89 let onSuccess:(response:RelationshipModel) => void = (relation:RelationshipModel) => {
90 link.setRelation(relation);
91 this.commonGraphUtils.insertLinkToGraph(cy, link, component.getRelationRequirementCapability.bind(component));
94 link.updateLinkDirection();
96 this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
97 () => component.createRelation(link.relation).then(onSuccess),
98 () => this.loaderService.hideLoader('composition-graph')
102 private createSimpleLink = (match:Match, cy:Cy.Instance, component:Component):void => {
103 let newRelation:RelationshipModel = match.matchToRelationModel();
104 let linkObg:CompositionCiLinkBase = this.linksFactory.createGraphLink(cy, newRelation, newRelation.relationships[0]);
105 this.createLink(linkObg, cy, component);
108 public batchDeleteEdges(cy: Cy.Instance, component: Component, edgesToDelete: Cy.CollectionEdges, alreadyDeleteNodeIds?: Array<string>): void {
109 let toDeleteLinks: Array<RelationshipModel> = new Array<RelationshipModel>();
110 if (alreadyDeleteNodeIds && alreadyDeleteNodeIds.length > 0) {
111 edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
112 if (alreadyDeleteNodeIds.indexOf(link.data().source) < 0 && alreadyDeleteNodeIds.indexOf(link.data().target) < 0) {
113 toDeleteLinks.push(link.data().relation);
118 edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
119 toDeleteLinks.push(link.data().relation);
122 this.loaderService.showLoader('composition-graph');
123 let onSuccessDeleteRelations = (response: Array<RelationshipModel>) => {
124 console.info('onSuccessDeleteRelations response is ', response);
125 //remove tempSimplePortNodes
126 if (alreadyDeleteNodeIds && alreadyDeleteNodeIds.length > 0) {
127 edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
128 if (alreadyDeleteNodeIds.indexOf(link.data().source) < 0 && alreadyDeleteNodeIds.indexOf(link.data().target) < 0) {
129 cy.remove(edgesToDelete);
134 edgesToDelete.each((i: number, link: Cy.CollectionEdges) => {
135 cy.remove(edgesToDelete);
140 if (toDeleteLinks.length > 0) {
141 this.generalGraphUtils.getGraphUtilsServerUpdateQueue().addBlockingUIActionWithReleaseCallback(
142 () => component.batchDeleteRelation(toDeleteLinks).then(onSuccessDeleteRelations),
143 () => this.loaderService.hideLoader('composition-graph'));
148 public createLinkFromMenu = (cy:Cy.Instance, chosenMatch:Match, component:Component):void => {
151 if (chosenMatch && chosenMatch instanceof Match) {
152 this.createSimpleLink(chosenMatch, cy, component);
159 * Filters the matches for UCPE links so that shown requirements and capabilites are only related to the selected ucpe-cp
162 * @param matchesArray
163 * @returns {Array<MatchBase>}
165 public filterUcpeLinks(fromNode:CompositionCiNodeBase, toNode:CompositionCiNodeBase, matchesArray:Array<Match>):any {
167 let matchLink:Array<Match>;
169 if (fromNode.isUcpePart) {
170 matchLink = _.filter(matchesArray, (match:Match) => {
171 return match.isOwner(fromNode.id);
175 if (toNode.isUcpePart) {
176 matchLink = _.filter(matchesArray, (match:Match) => {
177 return match.isOwner(toNode.id);
180 return matchLink ? matchLink : matchesArray;
185 * open the connect link menu if the link drawn is valid - match requirements & capabilities
191 public onLinkDrawn(cy:Cy.Instance, fromNode:Cy.CollectionFirstNode, toNode:Cy.CollectionFirstNode):ConnectRelationModel {
193 if (!this.commonGraphUtils.nodeLocationsCompatible(cy, fromNode, toNode)) {
196 let linkModel:Array<CompositionCiLinkBase> = this.generalGraphUtils.getAllCompositionCiLinks(cy);
198 let possibleRelations:Array<Match> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromNode.data().componentInstance,
199 toNode.data().componentInstance, linkModel);
201 //filter relations found to limit to specific ucpe-cp
202 possibleRelations = this.filterUcpeLinks(fromNode.data(), toNode.data(), possibleRelations);
204 //if found possibleRelations between the nodes we create relation menu directive and open the link menu
205 if (possibleRelations.length) {
206 // let menuPosition = this.generalGraphUtils.getLinkMenuPosition(cy, toNode.renderedPoint());
207 return new ConnectRelationModel(fromNode.data(), toNode.data(), possibleRelations);
214 * 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
215 * @param node - the node we dragged into or out of the ucpe
217 public deleteLinksWhenNodeMovedFromOrToUCPE(component:Component, cy:Cy.Instance, nodeMoved:Cy.CollectionNodes, vlsPendingDeletion?:Cy.CollectionNodes):void {
220 let linksToDelete:Cy.CollectionElements = cy.collection();
221 _.forEach(nodeMoved.neighborhood('node'), (neighborNode)=> {
223 if (neighborNode.data().isUcpePart) { //existing connections to ucpe or ucpe-cp - we want to delete even though nodeLocationsCompatible will technically return true
224 linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode)); // This will delete the ucpe-host-link, or the vl-ucpe-link if nodeMoved is vl
225 } else if (!this.commonGraphUtils.nodeLocationsCompatible(cy, nodeMoved, neighborNode)) { //connection to regular node or vl - check if locations are compatible
226 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
227 linksToDelete = linksToDelete.add(nodeMoved.edgesWith(neighborNode));
232 linksToDelete.each((i, link)=> {
233 this.deleteLink(cy, component, false, link);
239 * Creates a hostedOn link between a VF and UCPE
245 public createVfToUcpeLink = (component:Component, cy:Cy.Instance, ucpeNode:NodeUcpe, vfNode:CompositionCiNodeVf):void => {
246 let hostedOnMatch:Match = this.generalGraphUtils.canBeHostedOn(cy, ucpeNode.componentInstance, vfNode.componentInstance);
247 /* create relation */
248 let newRelation = new RelationshipModel();
249 newRelation.fromNode = ucpeNode.id;
250 newRelation.toNode = vfNode.id;
252 let link:CompositionCiLinkBase = this.linksFactory.createUcpeHostLink(newRelation);
253 link.relation = hostedOnMatch.matchToRelationModel();
254 this.createLink(link, cy, component);
257 private handlePathLink(cy:Cy.Instance, event:Cy.EventObject) {
258 let linkData = event.cyTarget.data();
259 let selectedPathId = linkData.pathId;
260 let pathEdges = cy.collection(`[pathId='${selectedPathId}']`);
261 if (pathEdges.length > 1) {
268 private handleVLLink(event:Cy.EventObject) {
269 let vl:Cy.CollectionNodes = event.cyTarget[0].target('.vl-node');
270 let connectedEdges:Cy.CollectionEdges = vl.connectedEdges(`[type!="${CompositionCiServicePathLink.LINK_TYPE}"]`);
271 if (vl.length && connectedEdges.length > 1) {
274 connectedEdges.select();
281 * Handles click event on links.
282 * If one edge selected: do nothing.
283 * Two or more edges: first click - select all, secondary click - select single.
287 public handleLinkClick(cy:Cy.Instance, event:Cy.EventObject) {
288 if (cy.$('edge:selected').length > 1 && event.cyTarget[0].selected()) {
289 cy.$(':selected').unselect();
291 if (event.cyTarget[0].data().type === CompositionCiServicePathLink.LINK_TYPE) {
292 this.handlePathLink(cy, event);
295 this.handleVLLink(event);
302 * Calculates the position for the menu that modifies an existing link
304 * @param elementWidth
305 * @param elementHeight
308 public calculateLinkMenuPosition(event, elementWidth, elementHeight):Point {
309 let point:Point = new Point(event.originalEvent.clientX, event.originalEvent.clientY);
310 if (event.originalEvent.view.screen.height - elementHeight < point.y) {
311 point.y = event.originalEvent.view.screen.height - elementHeight;
313 if (event.originalEvent.view.screen.width - elementWidth < point.x) {
314 point.x = event.originalEvent.view.screen.width - elementWidth;
321 * Gets the menu that is displayed when you click an existing link.
324 * @returns {LinkMenu}
326 public getModifyLinkMenu(link:Cy.CollectionFirstEdge, event:Cy.EventObject):LinkMenu {
327 let point:Point = this.calculateLinkMenuPosition(event, GraphUIObjects.MENU_LINK_VL_WIDTH_OFFSET, GraphUIObjects.MENU_LINK_VL_HEIGHT_OFFSET);
328 let menu:LinkMenu = new LinkMenu(point, true, link);
335 CompositionGraphLinkUtils.$inject = [
338 'CompositionGraphGeneralUtils',
340 'MatchCapabilitiesRequirementsUtils'