Catalog alignment
[sdc.git] / catalog-ui / src / app / ng2 / pages / 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, Match, CompositionCiLinkBase, CompositionCiNodeUcpeCp} from "app/models";
23 import {Dictionary, GraphUIObjects} from "app/utils";
24 import {MatchCapabilitiesRequirementsUtils} from "./match-capability-requierment-utils";
25 import {CommonGraphUtils} from "../common/common-graph-utils";
26 import {Injectable} from "@angular/core";
27 import {QueueServiceUtils} from "app/ng2/utils/queue-service-utils";
28 import {ComponentServiceNg2} from "app/ng2/services/component-services/component.service";
29 import {RequirementsGroup} from "app/models/requirement";
30 import {CapabilitiesGroup} from "app/models/capability";
31 import {TopologyTemplateService} from "app/ng2/services/component-services/topology-template.service";
32 import {CompositionService} from "../../composition.service";
33 import {WorkspaceService} from "app/ng2/pages/workspace/workspace.service";
34 import {NotificationsService} from "onap-ui-angular/dist/notifications/services/notifications.service";
35 import {NotificationSettings} from "onap-ui-angular/dist/notifications/utilities/notification.config";
36
37 export interface RequirementAndCapabilities {
38     capabilities: CapabilitiesGroup;
39     requirements: RequirementsGroup;
40 }
41
42 @Injectable()
43 export class CompositionGraphGeneralUtils {
44
45     public componentRequirementsAndCapabilitiesCaching = new Dictionary<string, RequirementAndCapabilities>();
46
47     constructor(private commonGraphUtils: CommonGraphUtils,
48                 private matchCapabilitiesRequirementsUtils: MatchCapabilitiesRequirementsUtils,
49                 private queueServiceUtils: QueueServiceUtils,
50                 private componentService: ComponentServiceNg2,
51                 private topologyTemplateService: TopologyTemplateService,
52                 private compositionService: CompositionService,
53                 private workspaceService: WorkspaceService) {
54     }
55
56     /**
57      * Get the offset for the link creation Menu
58      * @param point
59      * @returns {Cy.Position}
60      */
61     public calcMenuOffset: Function = (point: Cy.Position): Cy.Position => {
62         point.x = point.x + 60;
63         point.y = point.y + 105;
64         return point;
65     };
66
67     /**
68      *  return the top left position of the link menu
69      * @param cy
70      * @param targetNodePosition
71      * @returns {Cy.Position}
72      */
73     public getLinkMenuPosition = (cy: Cy.Instance, targetNodePosition: Cy.Position) => {
74         let menuPosition: Cy.Position = this.calcMenuOffset(targetNodePosition); //get the link mid point
75         if ($(document.body).height() < menuPosition.y + GraphUIObjects.LINK_MENU_HEIGHT + $(document.getElementsByClassName('sdc-composition-graph-wrapper')).offset().top) { // if position menu is overflow bottom
76             menuPosition.y = $(document.body).height() - GraphUIObjects.TOP_HEADER_HEIGHT - GraphUIObjects.LINK_MENU_HEIGHT;
77         }
78         return menuPosition;
79     };
80
81     public zoomGraphTo = (cy: Cy.Instance, zoomLevel: number): void => {
82         let zy = cy.height() / 2;
83         let zx = cy.width() / 2;
84         cy.zoom({
85             level: zoomLevel,
86             renderedPosition: {x: zx, y: zy}
87         });
88     }
89
90     //saves the current zoom, and then sets a temporary maximum zoom for zoomAll, and then reverts to old value
91     public zoomAllWithMax = (cy: Cy.Instance, maxZoom: number): void => {
92
93         let oldMaxZoom: number = cy.maxZoom();
94
95         cy.maxZoom(maxZoom);
96         this.zoomAll(cy);
97         cy.maxZoom(oldMaxZoom);
98
99     };
100
101     //Zooms to fit all of the nodes in the collection passed in. If no nodes are passed in, will zoom to fit all nodes on graph
102     public zoomAll = (cy: Cy.Instance, nodes?: Cy.CollectionNodes): void => {
103
104         if (!nodes || !nodes.length) {
105             nodes = cy.nodes();
106         }
107
108         cy.resize();
109         cy.animate({
110             fit: {eles: nodes, padding: 20},
111             center: {eles: nodes}
112         }, {duration: 400});
113     };
114
115     /**
116      * will return true/false if two nodes overlapping
117      *
118      * @param graph node
119      */
120     private isNodesOverlapping(node: Cy.CollectionFirstNode, draggedNode: Cy.CollectionFirstNode): boolean {
121
122         let nodeBoundingBox: Cy.BoundingBox = node.renderedBoundingBox();
123         let secondNodeBoundingBox: Cy.BoundingBox = draggedNode.renderedBoundingBox();
124
125         return this.isBBoxOverlapping(nodeBoundingBox, secondNodeBoundingBox);
126     }
127
128     /**
129      * Checks whether the bounding boxes of two nodes are overlapping on any side
130      * @param nodeOneBBox
131      * @param nodeTwoBBox
132      * @returns {boolean}
133      */
134     private isBBoxOverlapping(nodeOneBBox: Cy.BoundingBox, nodeTwoBBox: Cy.BoundingBox) {
135         return (((nodeOneBBox.x1 < nodeTwoBBox.x1 && nodeOneBBox.x2 > nodeTwoBBox.x1) ||
136             (nodeOneBBox.x1 < nodeTwoBBox.x2 && nodeOneBBox.x2 > nodeTwoBBox.x2) ||
137             (nodeTwoBBox.x1 < nodeOneBBox.x1 && nodeTwoBBox.x2 > nodeOneBBox.x2)) &&
138             ((nodeOneBBox.y1 < nodeTwoBBox.y1 && nodeOneBBox.y2 > nodeTwoBBox.y1) ||
139                 (nodeOneBBox.y1 < nodeTwoBBox.y2 && nodeOneBBox.y2 > nodeTwoBBox.y2) ||
140                 (nodeTwoBBox.y1 < nodeOneBBox.y1 && nodeTwoBBox.y2 > nodeOneBBox.y2)))
141     }
142
143     /**
144      * Checks whether a specific topologyTemplate instance can be hosted on the UCPE instance
145      * @param cy - Cytoscape instance
146      * @param fromUcpeInstance
147      * @param toComponentInstance
148      * @returns {Match}
149      */
150     public canBeHostedOn(cy: Cy.Instance, fromUcpeInstance: ComponentInstance, toComponentInstance: ComponentInstance): Match {
151
152         let matches: Array<Match> = this.matchCapabilitiesRequirementsUtils.getMatchedRequirementsCapabilities(fromUcpeInstance, toComponentInstance, this.getAllCompositionCiLinks(cy));
153         let hostedOnMatch: Match = _.find(matches, (match: Match) => {
154             return match.requirement.capability.toLowerCase() === 'tosca.capabilities.container';
155         });
156
157         return hostedOnMatch;
158     };
159
160     /**
161      * Checks whether node can be dropped into UCPE
162      * @param cy
163      * @param nodeToInsert
164      * @param ucpeNode
165      * @returns {boolean}
166      */
167     private isValidDropInsideUCPE(cy: Cy.Instance, nodeToInsert: ComponentInstance, ucpeNode: ComponentInstance): boolean {
168
169         let hostedOnMatch: Match = this.canBeHostedOn(cy, ucpeNode, nodeToInsert);
170         let result: boolean = !angular.isUndefined(hostedOnMatch) || nodeToInsert.isVl(); //group validation
171         return result;
172
173     };
174
175     /**
176      * For drops from palette, checks whether the node can be dropped. If node is being held over another node, check if capable of hosting
177      * @param cy
178      * @param pseudoNodeBBox
179      * @param paletteComponentInstance
180      * @returns {boolean}
181      */
182     public isPaletteDropValid(cy: Cy.Instance, pseudoNodeBBox: Cy.BoundingBox) {
183
184         let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => {
185             if (this.isBBoxOverlapping(pseudoNodeBBox, graphNode.renderedBoundingBox())) {
186                 return true;
187             }
188             return false;
189         });
190
191         return illegalOverlappingNodes.length === 0;
192     }
193
194     /**
195      * will return true/false if a drop of a single node is valid
196      *
197      * @param graph node
198      */
199     public isValidDrop(cy: Cy.Instance, draggedNode: Cy.CollectionFirstNode): boolean {
200
201         let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => { //all sdc nodes, removing child nodes (childe node allways collaps
202
203             if (draggedNode.data().isUcpe && (graphNode.isChild() || graphNode.data().isInsideGroup)) { //ucpe cps always inside ucpe, no overlapping
204                 return false;
205             }
206             if (draggedNode.data().isInsideGroup && (!draggedNode.active() || graphNode.data().isUcpe)) {
207                 return false;
208             }
209
210             if (!draggedNode.data().isUcpe && !(draggedNode.data() instanceof CompositionCiNodeUcpeCp) && graphNode.data().isUcpe) { //case we are dragging a node into UCPE
211                 let isEntirelyInUCPE: boolean = this.commonGraphUtils.isFirstBoxContainsInSecondBox(draggedNode.renderedBoundingBox(), graphNode.renderedBoundingBox());
212                 if (isEntirelyInUCPE) {
213                     if (this.isValidDropInsideUCPE(cy, draggedNode.data().componentInstance, graphNode.data().componentInstance)) { //if this is valid insert into ucpe, we return false - no illegal overlapping nodes
214                         return false;
215                     }
216                 }
217             }
218             return graphNode.data().id !== draggedNode.data().id && this.isNodesOverlapping(draggedNode, graphNode);
219
220         });
221         // return false;
222         return illegalOverlappingNodes.length === 0;
223     };
224
225     /**
226      * will return true/false if the move of the nodes is valid (no node overlapping and verifying if insert into UCPE is valid)
227      *
228      * @param  nodesArray - the selected drags nodes
229      */
230     public isGroupValidDrop(cy: Cy.Instance, nodesArray: Cy.CollectionNodes): boolean {
231         let filterDraggedNodes = nodesArray.filter('[?isDraggable]');
232         let isValidDrop = _.every(filterDraggedNodes, (node: Cy.CollectionFirstNode) => {
233             return this.isValidDrop(cy, node);
234
235         });
236         return isValidDrop;
237     };
238
239     /**
240      * get all links in diagram
241      * @param cy
242      * @returns {any[]|boolean[]}
243      */
244     public getAllCompositionCiLinks = (cy: Cy.Instance): Array<CompositionCiLinkBase> => {
245         return _.map(cy.edges("[isSdcElement]"), (edge: Cy.CollectionEdges) => {
246             return edge.data();
247         });
248     };
249
250     /**
251      *
252      * @param blockAction - true/false if this is a block action
253      * @param instances
254      * @param component
255      */
256     public pushMultipleUpdateComponentInstancesRequestToQueue = (instances: Array<ComponentInstance>): void => {
257         this.queueServiceUtils.addNonBlockingUIAction(() => {
258             return new Promise<boolean>((resolve, reject) => {
259                 let uniqueId = this.workspaceService.metadata.uniqueId;
260                 let topologyType = this.workspaceService.metadata.componentType;
261                 this.topologyTemplateService.updateMultipleComponentInstances(uniqueId, topologyType, instances).subscribe(instancesResult => {
262                     this.compositionService.updateComponentInstances(instancesResult);
263                     resolve(true);
264                 });
265             });
266         });
267     }
268 }