Sync Integ to Master
[sdc.git] / catalog-ui / src / app / directives / graphs-v2 / composition-graph / utils / composition-graph-general-utils.ts
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 import * as _ from "lodash";
22 import {ComponentInstance, Component, Match, CompositionCiLinkBase, CompositionCiNodeUcpeCp} from "app/models";
23 import {QueueUtils, Dictionary, GraphUIObjects} from "app/utils";
24 import {LoaderService} from "app/services";
25 import {MatchCapabilitiesRequirementsUtils} from "./match-capability-requierment-utils";
26 import {CommonGraphUtils} from "../../common/common-graph-utils";
27
28
29 export class CompositionGraphGeneralUtils {
30
31     public componentRequirementsAndCapabilitiesCaching = new Dictionary<string, Component>();
32     protected static graphUtilsUpdateQueue:QueueUtils;
33
34     constructor(private $q:ng.IQService,
35                 private LoaderService:LoaderService,
36                 private commonGraphUtils:CommonGraphUtils,
37                 private matchCapabilitiesRequirementsUtils:MatchCapabilitiesRequirementsUtils) {
38         CompositionGraphGeneralUtils.graphUtilsUpdateQueue = new QueueUtils(this.$q);
39     }
40
41
42     /**
43      * Get the offset for the link creation Menu
44      * @param point
45      * @returns {Cy.Position}
46      */
47     public calcMenuOffset:Function = (point:Cy.Position):Cy.Position => {
48         point.x = point.x + 60;
49         point.y = point.y + 105;
50         return point;
51     };
52
53     /**
54      *  return the top left position of the link menu
55      * @param cy
56      * @param targetNodePosition
57      * @returns {Cy.Position}
58      */
59     public getLinkMenuPosition = (cy:Cy.Instance, targetNodePosition:Cy.Position) => {
60         let menuPosition:Cy.Position = this.calcMenuOffset(targetNodePosition); //get the link mid point
61         if ($(document.body).height() < menuPosition.y + GraphUIObjects.LINK_MENU_HEIGHT + $(document.getElementsByClassName('sdc-composition-graph-wrapper')).offset().top) { // if position menu is overflow bottom
62             menuPosition.y = $(document.body).height() - GraphUIObjects.TOP_HEADER_HEIGHT - GraphUIObjects.LINK_MENU_HEIGHT;
63         }
64         return menuPosition;
65     };
66
67
68     public zoomGraphTo = (cy:Cy.Instance, zoomLevel: number):void => {
69         let zy = cy.height() / 2;
70         let zx = cy.width() / 2;
71         cy.zoom({
72             level: zoomLevel,
73             renderedPosition: { x: zx, y: zy }
74         });
75     }
76     /**
77      * will return true/false if two nodes overlapping
78      *
79      * @param graph node
80      */
81     private isNodesOverlapping(node:Cy.CollectionFirstNode, draggedNode:Cy.CollectionFirstNode):boolean {
82
83         let nodeBoundingBox:Cy.BoundingBox = node.renderedBoundingBox();
84         let secondNodeBoundingBox:Cy.BoundingBox = draggedNode.renderedBoundingBox();
85
86         return this.isBBoxOverlapping(nodeBoundingBox, secondNodeBoundingBox);
87     }
88
89     /**
90      * Checks whether the bounding boxes of two nodes are overlapping on any side
91      * @param nodeOneBBox
92      * @param nodeTwoBBox
93      * @returns {boolean}
94      */
95     private isBBoxOverlapping(nodeOneBBox:Cy.BoundingBox, nodeTwoBBox:Cy.BoundingBox) {
96         return (((nodeOneBBox.x1 < nodeTwoBBox.x1 && nodeOneBBox.x2 > nodeTwoBBox.x1) ||
97         (nodeOneBBox.x1 < nodeTwoBBox.x2 && nodeOneBBox.x2 > nodeTwoBBox.x2) ||
98         (nodeTwoBBox.x1 < nodeOneBBox.x1 && nodeTwoBBox.x2 > nodeOneBBox.x2)) &&
99         ((nodeOneBBox.y1 < nodeTwoBBox.y1 && nodeOneBBox.y2 > nodeTwoBBox.y1) ||
100         (nodeOneBBox.y1 < nodeTwoBBox.y2 && nodeOneBBox.y2 > nodeTwoBBox.y2) ||
101         (nodeTwoBBox.y1 < nodeOneBBox.y1 && nodeTwoBBox.y2 > nodeOneBBox.y2)))
102     }
103
104
105     /**
106      * Checks whether a specific component instance can be hosted on the UCPE instance
107      * @param cy - Cytoscape instance
108      * @param fromUcpeInstance
109      * @param toComponentInstance
110      * @returns {Match}
111      */
112     public canBeHostedOn(cy:Cy.Instance, fromUcpeInstance:ComponentInstance, toComponentInstance:ComponentInstance):Match {
113
114         let matches:Array<Match> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromUcpeInstance, toComponentInstance, this.getAllCompositionCiLinks(cy));
115         let hostedOnMatch:Match = _.find(matches, (match:Match) => {
116             return match.requirement.capability.toLowerCase() === 'tosca.capabilities.container';
117         });
118
119         return hostedOnMatch;
120     };
121
122
123     /**
124      * Checks whether node can be dropped into UCPE
125      * @param cy
126      * @param nodeToInsert
127      * @param ucpeNode
128      * @returns {boolean}
129      */
130     private isValidDropInsideUCPE(cy:Cy.Instance, nodeToInsert:ComponentInstance, ucpeNode:ComponentInstance):boolean {
131
132         let hostedOnMatch:Match = this.canBeHostedOn(cy, ucpeNode, nodeToInsert);
133         let result:boolean = !angular.isUndefined(hostedOnMatch) || nodeToInsert.isVl(); //group validation
134         return result;
135
136     };
137
138
139     /**
140      * For drops from palette, checks whether the node can be dropped. If node is being held over another node, check if capable of hosting
141      * @param cy
142      * @param pseudoNodeBBox
143      * @param paletteComponentInstance
144      * @returns {boolean}
145      */
146     public isPaletteDropValid(cy:Cy.Instance, pseudoNodeBBox:Cy.BoundingBox, paletteComponentInstance:ComponentInstance) {
147
148         let componentIsUCPE:boolean = (paletteComponentInstance.capabilities && paletteComponentInstance.capabilities['tosca.capabilities.Container'] && paletteComponentInstance.name.toLowerCase().indexOf('ucpe') > -1);
149
150         if (componentIsUCPE && cy.nodes('[?isUcpe]').length > 0) { //second UCPE not allowed
151             return false;
152         }
153
154         let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode:Cy.CollectionFirstNode) => {
155
156             if (this.isBBoxOverlapping(pseudoNodeBBox, graphNode.renderedBoundingBox())) {
157                 if (!componentIsUCPE && graphNode.data().isUcpe) {
158                     return !this.isValidDropInsideUCPE(cy, paletteComponentInstance, graphNode.data().componentInstance); //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
159                 }
160                 return true;
161             }
162
163             return false;
164         });
165
166         return illegalOverlappingNodes.length === 0;
167     }
168
169     /**
170      * will return true/false if a drop of a single node is valid
171      *
172      * @param graph node
173      */
174     public isValidDrop(cy:Cy.Instance, draggedNode:Cy.CollectionFirstNode):boolean {
175
176         let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode:Cy.CollectionFirstNode) => { //all sdc nodes, removing child nodes (childe node allways collaps
177
178             if (draggedNode.data().isUcpe && (graphNode.isChild() || graphNode.data().isInsideGroup)) { //ucpe cps always inside ucpe, no overlapping
179                 return false;
180             }
181             if (draggedNode.data().isInsideGroup && (!draggedNode.active() || graphNode.data().isUcpe)) {
182                 return false;
183             }
184
185             if (!draggedNode.data().isUcpe && !(draggedNode.data() instanceof CompositionCiNodeUcpeCp) && graphNode.data().isUcpe) { //case we are dragging a node into UCPE
186                 let isEntirelyInUCPE:boolean = this.commonGraphUtils.isFirstBoxContainsInSecondBox(draggedNode.renderedBoundingBox(), graphNode.renderedBoundingBox());
187                 if (isEntirelyInUCPE) {
188                     if (this.isValidDropInsideUCPE(cy, draggedNode.data().componentInstance, graphNode.data().componentInstance)) { //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
189                         return false;
190                     }
191                 }
192             }
193             return graphNode.data().id !== draggedNode.data().id && this.isNodesOverlapping(draggedNode, graphNode);
194
195         });
196         // return false;
197         return illegalOverlappingNodes.length === 0;
198     };
199
200     /**
201      * will return true/false if the move of the nodes is valid (no node overlapping and verifying if insert into UCPE is valid)
202      *
203      * @param  nodesArray - the selected drags nodes
204      */
205     public isGroupValidDrop(cy:Cy.Instance, nodesArray:Cy.CollectionNodes):boolean {
206         let filterDraggedNodes = nodesArray.filter('[?isDraggable]');
207         let isValidDrop = _.every(filterDraggedNodes, (node:Cy.CollectionFirstNode) => {
208             return this.isValidDrop(cy, node);
209
210         });
211         return isValidDrop;
212     };
213
214     /**
215      * get all links in diagram
216      * @param cy
217      * @returns {any[]|boolean[]}
218      */
219     public getAllCompositionCiLinks = (cy:Cy.Instance):Array<CompositionCiLinkBase> => {
220         return _.map(cy.edges("[isSdcElement]"), (edge:Cy.CollectionEdges) => {
221             return edge.data();
222         });
223     };
224
225
226     /**
227      * Get Graph Utils server queue
228      * @returns {QueueUtils}
229      */
230     public getGraphUtilsServerUpdateQueue():QueueUtils {
231         return CompositionGraphGeneralUtils.graphUtilsUpdateQueue;
232     }
233     ;
234
235     /**
236      *
237      * @param blockAction - true/false if this is a block action
238      * @param instances
239      * @param component
240      */
241     public pushMultipleUpdateComponentInstancesRequestToQueue = (blockAction:boolean, instances:Array<ComponentInstance>, component:Component):void => {
242         if (blockAction) {
243             this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
244                 () => component.updateMultipleComponentInstances(instances)
245             );
246         } else {
247             this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
248                 () => component.updateMultipleComponentInstances(instances),
249                 () => this.LoaderService.hideLoader('composition-graph'));
250         }
251     };
252
253     /**
254      * this function will update component instance data
255      * @param blockAction - true/false if this is a block action
256      * @param updatedInstance
257      */
258     public pushUpdateComponentInstanceActionToQueue = (component:Component, blockAction:boolean, updatedInstance:ComponentInstance):void => {
259
260         if (blockAction) {
261             this.LoaderService.showLoader('composition-graph');
262             this.getGraphUtilsServerUpdateQueue().addBlockingUIAction(
263                 () => component.updateComponentInstance(updatedInstance)
264             );
265         } else {
266             this.getGraphUtilsServerUpdateQueue().addNonBlockingUIAction(
267                 () => component.updateComponentInstance(updatedInstance),
268                 () => this.LoaderService.hideLoader('composition-graph'));
269         }
270     };
271 }
272
273 CompositionGraphGeneralUtils.$inject = ['$q', 'LoaderService', 'CommonGraphUtils', 'MatchCapabilitiesRequirementsUtils'];