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=========================================================
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";
37 export interface RequirementAndCapabilities {
38 capabilities: CapabilitiesGroup;
39 requirements: RequirementsGroup;
43 export class CompositionGraphGeneralUtils {
45 public componentRequirementsAndCapabilitiesCaching = new Dictionary<string, RequirementAndCapabilities>();
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) {
57 * Get the offset for the link creation Menu
59 * @returns {Cy.Position}
61 public calcMenuOffset: Function = (point: Cy.Position): Cy.Position => {
62 point.x = point.x + 60;
63 point.y = point.y + 105;
68 * return the top left position of the link menu
70 * @param targetNodePosition
71 * @returns {Cy.Position}
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;
81 public zoomGraphTo = (cy: Cy.Instance, zoomLevel: number): void => {
82 let zy = cy.height() / 2;
83 let zx = cy.width() / 2;
86 renderedPosition: {x: zx, y: zy}
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 => {
93 let oldMaxZoom: number = cy.maxZoom();
97 cy.maxZoom(oldMaxZoom);
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 => {
104 if (!nodes || !nodes.length) {
110 fit: {eles: nodes, padding: 20},
111 center: {eles: nodes}
116 * will return true/false if two nodes overlapping
120 private isNodesOverlapping(node: Cy.CollectionFirstNode, draggedNode: Cy.CollectionFirstNode): boolean {
122 let nodeBoundingBox: Cy.BoundingBox = node.renderedBoundingBox();
123 let secondNodeBoundingBox: Cy.BoundingBox = draggedNode.renderedBoundingBox();
125 return this.isBBoxOverlapping(nodeBoundingBox, secondNodeBoundingBox);
129 * Checks whether the bounding boxes of two nodes are overlapping on any side
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)))
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
150 public canBeHostedOn(cy: Cy.Instance, fromUcpeInstance: ComponentInstance, toComponentInstance: ComponentInstance): Match {
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';
157 return hostedOnMatch;
161 * Checks whether node can be dropped into UCPE
163 * @param nodeToInsert
167 private isValidDropInsideUCPE(cy: Cy.Instance, nodeToInsert: ComponentInstance, ucpeNode: ComponentInstance): boolean {
169 let hostedOnMatch: Match = this.canBeHostedOn(cy, ucpeNode, nodeToInsert);
170 let result: boolean = !angular.isUndefined(hostedOnMatch) || nodeToInsert.isVl(); //group validation
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
178 * @param pseudoNodeBBox
179 * @param paletteComponentInstance
182 public isPaletteDropValid(cy: Cy.Instance, pseudoNodeBBox: Cy.BoundingBox) {
184 let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => {
185 if (this.isBBoxOverlapping(pseudoNodeBBox, graphNode.renderedBoundingBox())) {
191 return illegalOverlappingNodes.length === 0;
195 * will return true/false if a drop of a single node is valid
199 public isValidDrop(cy: Cy.Instance, draggedNode: Cy.CollectionFirstNode): boolean {
201 let illegalOverlappingNodes = _.filter(cy.nodes("[isSdcElement]"), (graphNode: Cy.CollectionFirstNode) => { //all sdc nodes, removing child nodes (childe node allways collaps
203 if (draggedNode.data().isUcpe && (graphNode.isChild() || graphNode.data().isInsideGroup)) { //ucpe cps always inside ucpe, no overlapping
206 if (draggedNode.data().isInsideGroup && (!draggedNode.active() || graphNode.data().isUcpe)) {
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
218 return graphNode.data().id !== draggedNode.data().id && this.isNodesOverlapping(draggedNode, graphNode);
222 return illegalOverlappingNodes.length === 0;
226 * will return true/false if the move of the nodes is valid (no node overlapping and verifying if insert into UCPE is valid)
228 * @param nodesArray - the selected drags nodes
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);
240 * get all links in diagram
242 * @returns {any[]|boolean[]}
244 public getAllCompositionCiLinks = (cy: Cy.Instance): Array<CompositionCiLinkBase> => {
245 return _.map(cy.edges("[isSdcElement]"), (edge: Cy.CollectionEdges) => {
252 * @param blockAction - true/false if this is a block action
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);