2 * Created by ob0695 on 4/24/2018.
4 import { AfterViewInit, Component, ElementRef, HostBinding, Input } from '@angular/core';
5 import { Select, Store } from '@ngxs/store';
8 Component as TopologyTemplate,
10 CompositionCiNodeBase,
25 ZoneInstanceAssignmentType,
29 import { ForwardingPath } from 'app/models/forwarding-path';
30 import { CompositionCiServicePathLink } from 'app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link';
31 import { UIZoneInstanceObject } from 'app/models/ui-models/ui-zone-instance-object';
32 import { CompositionService } from 'app/ng2/pages/composition/composition.service';
33 import { CommonGraphUtils } from 'app/ng2/pages/composition/graph/common/common-graph-utils';
34 import { ComponentInstanceNodesStyle } from 'app/ng2/pages/composition/graph/common/style/component-instances-nodes-style';
35 import { ConnectionPropertiesViewComponent } from 'app/ng2/pages/composition/graph/connection-wizard/connection-properties-view/connection-properties-view.component';
36 import { ConnectionWizardHeaderComponent } from 'app/ng2/pages/composition/graph/connection-wizard/connection-wizard-header/connection-wizard-header.component';
37 import { ConnectionWizardService } from 'app/ng2/pages/composition/graph/connection-wizard/connection-wizard.service';
38 import { FromNodeStepComponent } from 'app/ng2/pages/composition/graph/connection-wizard/from-node-step/from-node-step.component';
39 import { PropertiesStepComponent } from 'app/ng2/pages/composition/graph/connection-wizard/properties-step/properties-step.component';
40 import { ToNodeStepComponent } from 'app/ng2/pages/composition/graph/connection-wizard/to-node-step/to-node-step.component';
41 import { WorkspaceService } from 'app/ng2/pages/workspace/workspace.service';
42 import { ComponentInstanceServiceNg2 } from 'app/ng2/services/component-instance-services/component-instance.service';
43 import { TopologyTemplateService } from 'app/ng2/services/component-services/topology-template.service';
44 import { ModalService } from 'app/ng2/services/modal.service';
45 import { ComponentGenericResponse } from 'app/ng2/services/responses/component-generic-response';
46 import { ServiceGenericResponse } from 'app/ng2/services/responses/service-generic-response';
47 import { WorkspaceState } from 'app/ng2/store/states/workspace.state';
48 import { EventListenerService } from 'app/services';
49 import { ComponentInstanceFactory, EVENTS, SdcElementType } from 'app/utils';
50 import { ComponentType, GRAPH_EVENTS, GraphColors, DEPENDENCY_EVENTS , SUBSTITUTION_FILTER_EVENTS} from 'app/utils/constants';
51 import * as _ from 'lodash';
52 import { DndDropEvent } from 'ngx-drag-drop/ngx-drag-drop';
53 import { SdcUiServices } from 'onap-ui-angular';
54 import { NotificationSettings } from 'onap-ui-angular/dist/notifications/utilities/notification.config';
55 import { menuItem } from 'onap-ui-angular/dist/simple-popup-menu/menu-data.interface';
56 import { CytoscapeEdgeEditation } from '../../../../../third-party/cytoscape.js-edge-editation/CytoscapeEdgeEditation.js';
57 import { SelectedComponentType, SetSelectedComponentAction } from '../common/store/graph.actions';
58 import { GraphState } from '../common/store/graph.state';
60 CompositionGraphGeneralUtils,
61 CompositionGraphNodesUtils,
62 CompositionGraphZoneUtils,
63 MatchCapabilitiesRequirementsUtils
65 import { CompositionGraphLinkUtils } from './utils/composition-graph-links-utils';
66 import { CompositionGraphPaletteUtils } from './utils/composition-graph-palette-utils';
67 import { ServicePathGraphUtils } from './utils/composition-graph-service-path-utils';
69 declare const window: any;
72 selector: 'composition-graph',
73 templateUrl: './composition-graph.component.html',
74 styleUrls: ['./composition-graph.component.less']
77 export class CompositionGraphComponent implements AfterViewInit {
79 @Select(WorkspaceState.isViewOnly) isViewOnly$: boolean;
80 @Select(GraphState.withSidebar) withSidebar$: boolean;
81 @Input() topologyTemplate: TopologyTemplate;
82 @HostBinding('attr.data-tests-id') dataTestId: string;
83 @Input() testId: string;
85 // tslint:disable:variable-name
86 private _cy: Cy.Instance;
87 private zoneTagMode: string;
88 private activeZoneInstance: ZoneInstance;
89 private zones: Zone[];
90 private currentlyClickedNodePosition: Cy.Position;
91 private dragElement: JQuery;
92 private dragComponent: ComponentInstance;
93 private componentInstanceNames: string[];
94 private topologyTemplateId: string;
95 private topologyTemplateType: string;
97 constructor(private elRef: ElementRef,
98 private nodesFactory: NodesFactory,
99 private eventListenerService: EventListenerService,
100 private compositionGraphZoneUtils: CompositionGraphZoneUtils,
101 private generalGraphUtils: CompositionGraphGeneralUtils,
102 private compositionGraphLinkUtils: CompositionGraphLinkUtils,
103 private nodesGraphUtils: CompositionGraphNodesUtils,
104 private connectionWizardService: ConnectionWizardService,
105 private commonGraphUtils: CommonGraphUtils,
106 private modalService: ModalService,
107 private compositionGraphPaletteUtils: CompositionGraphPaletteUtils,
108 private topologyTemplateService: TopologyTemplateService,
109 private componentInstanceService: ComponentInstanceServiceNg2,
110 private matchCapabilitiesRequirementsUtils: MatchCapabilitiesRequirementsUtils,
111 private store: Store,
112 private compositionService: CompositionService,
113 private loaderService: SdcUiServices.LoaderService,
114 private workspaceService: WorkspaceService,
115 private notificationService: SdcUiServices.NotificationsService,
116 private simplePopupMenuService: SdcUiServices.simplePopupMenuService,
117 private servicePathGraphUtils: ServicePathGraphUtils) {
121 this.dataTestId = this.testId;
122 this.topologyTemplateId = this.workspaceService.metadata.uniqueId;
123 this.topologyTemplateType = this.workspaceService.metadata.componentType;
125 this.store.dispatch(new SetSelectedComponentAction({
126 component: this.topologyTemplate,
127 type: SelectedComponentType.TOPOLOGY_TEMPLATE
129 this.eventListenerService.registerObserverCallback(EVENTS.ON_CHECKOUT, () => {
130 this.loadGraphData();
132 this.loadCompositionData();
141 _.forEach(GRAPH_EVENTS, (event) => {
142 this.eventListenerService.unRegisterObserver(event);
144 this.eventListenerService.unRegisterObserver(EVENTS.ON_CHECKOUT);
145 this.eventListenerService.unRegisterObserver(DEPENDENCY_EVENTS.ON_DEPENDENCY_CHANGE);
146 this.eventListenerService.unRegisterObserver(SUBSTITUTION_FILTER_EVENTS.ON_SUBSTITUTION_FILTER_CHANGE);
149 public isViewOnly = (): boolean => {
150 return this.store.selectSnapshot((state) => state.workspace.isViewOnly);
153 public zoom = (zoomIn: boolean): void => {
154 const currentZoom: number = this._cy.zoom();
156 this.generalGraphUtils.zoomGraphTo(this._cy, currentZoom + .1);
158 this.generalGraphUtils.zoomGraphTo(this._cy, currentZoom - .1);
162 public zoomAllWithoutSidebar = () => {
163 setTimeout(() => { // wait for sidebar changes to take effect before zooming
164 this.generalGraphUtils.zoomAll(this._cy);
168 public getAutoCompleteValues = (searchTerm: string) => {
169 if (searchTerm.length > 1) { // US requirement: only display search results after 2nd letter typed.
170 const nodes: Cy.CollectionNodes = this.nodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
171 this.componentInstanceNames = _.map(nodes, (node) => node.data('name'));
173 this.componentInstanceNames = [];
177 public highlightSearchMatches = (searchTerm: string) => {
178 this.nodesGraphUtils.highlightMatchingNodesByName(this._cy, searchTerm);
179 const matchingNodes: Cy.CollectionNodes = this.nodesGraphUtils.getMatchingNodesByName(this._cy, searchTerm);
180 this.generalGraphUtils.zoomAll(this._cy, matchingNodes);
183 public onDrop = (dndEvent: DndDropEvent) => {
184 this.compositionGraphPaletteUtils.addNodeFromPalette(this._cy, dndEvent);
187 public openServicePathMenu = ($event): void => {
189 const menuConfig: menuItem[] = [];
190 if (!this.isViewOnly()) {
192 text: 'Create Service Flow',
193 action: () => this.servicePathGraphUtils.onCreateServicePath()
197 text: 'Service Flows List',
199 action: () => this.servicePathGraphUtils.onListServicePath()
201 const popup = this.simplePopupMenuService.openBaseMenu(menuConfig, {
208 public deletePathsOnCy = () => {
209 this.servicePathGraphUtils.deletePathsFromGraph(this._cy);
212 public drawPathOnCy = (data: ForwardingPath) => {
213 this.servicePathGraphUtils.drawPath(this._cy, data);
216 public onTapEnd = (event: Cy.EventObject) => {
217 if (this.zoneTagMode) {
220 if (event.cyTarget === this._cy) { // On Background clicked
221 if (this._cy.$('node:selected').length === 0) { // if the background click but not dragged
222 if (this.activeZoneInstance) {
223 this.unsetActiveZoneInstance();
224 this.selectTopologyTemplate();
226 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_GRAPH_BACKGROUND_CLICKED);
227 this.selectTopologyTemplate();
231 } else if (event.cyTarget[0].isEdge()) { // and Edge clicked
232 this.compositionGraphLinkUtils.handleLinkClick(this._cy, event);
233 if (event.cyTarget[0].data().type === CompositionCiServicePathLink.LINK_TYPE) {
236 this.openModifyLinkMenu(this.compositionGraphLinkUtils.getModifyLinkMenu(event.cyTarget[0], event), event);
237 } else { // On Node clicked
239 this._cy.nodes(':grabbed').style({'overlay-opacity': 0});
241 const newPosition = event.cyTarget[0].position();
242 // node position changed (drop after drag event) - we need to update position
243 if (this.currentlyClickedNodePosition.x !== newPosition.x || this.currentlyClickedNodePosition.y !== newPosition.y) {
244 const nodesMoved: Cy.CollectionNodes = this._cy.$(':grabbed');
245 this.nodesGraphUtils.onNodesPositionChanged(this._cy, this.topologyTemplate, nodesMoved);
247 if (this.activeZoneInstance) {
248 this.unsetActiveZoneInstance();
250 this.selectComponentInstance(event.cyTarget[0].data().componentInstance);
255 private registerCytoscapeGraphEvents() {
257 this._cy.on('addedgemouseup', (event, data) => {
258 const connectRelationModel: ConnectRelationModel = this.compositionGraphLinkUtils.onLinkDrawn(this._cy, data.source, data.target);
259 if (connectRelationModel != null) {
260 this.connectionWizardService.setRelationMenuDirectiveObj(connectRelationModel);
261 this.connectionWizardService.selectedMatch = null;
263 const steps: StepModel[] = [];
264 const fromNodeName: string = connectRelationModel.fromNode.componentInstance.name;
265 const toNodeName: string = connectRelationModel.toNode.componentInstance.name;
266 steps.push(new StepModel(fromNodeName, FromNodeStepComponent));
267 steps.push(new StepModel(toNodeName, ToNodeStepComponent));
268 steps.push(new StepModel('Properties', PropertiesStepComponent));
269 const wizardTitle = 'Connect: ' + fromNodeName + ' to ' + toNodeName;
270 const modalInstance = this.modalService.createMultiStepsWizard(wizardTitle, steps, this.createLinkFromMenu, ConnectionWizardHeaderComponent);
271 modalInstance.instance.open();
275 this._cy.on('tapstart', 'node', (event: Cy.EventObject) => {
276 this.currentlyClickedNodePosition = angular.copy(event.cyTarget[0].position()); // update node position on drag
279 this._cy.on('drag', 'node', (event: Cy.EventObject) => {
280 if (event.cyTarget.data().componentSubType !== SdcElementType.POLICY && event.cyTarget.data().componentSubType !== SdcElementType.GROUP) {
281 event.cyTarget.style({'overlay-opacity': 0.24});
282 if (this.generalGraphUtils.isValidDrop(this._cy, event.cyTarget)) {
283 event.cyTarget.style({'overlay-color': GraphColors.NODE_BACKGROUND_COLOR});
285 event.cyTarget.style({'overlay-color': GraphColors.NODE_OVERLAPPING_BACKGROUND_COLOR});
290 this._cy.on('handlemouseover', (event, payload) => {
291 // no need to add opacity while we are dragging and hovering othe nodes- or if opacity was already calculated for these nodes
292 if (payload.node.grabbed() || this._cy.scratch('_edge_editation_highlights') === true) {
296 if (this.zoneTagMode) {
297 this.zoneTagMode = this.zones[this.activeZoneInstance.type].getHoverTagModeId();
301 const nodesData = this.nodesGraphUtils.getAllNodesData(this._cy.nodes());
302 const nodesLinks = this.generalGraphUtils.getAllCompositionCiLinks(this._cy);
303 const instance = payload.node.data().componentInstance;
304 const filteredNodesData = this.matchCapabilitiesRequirementsUtils.findMatchingNodesToComponentInstance(instance, nodesData, nodesLinks);
305 this.matchCapabilitiesRequirementsUtils.highlightMatchingComponents(filteredNodesData, this._cy);
306 this.matchCapabilitiesRequirementsUtils.fadeNonMachingComponents(filteredNodesData, nodesData, this._cy, payload.node.data());
308 this._cy.scratch()._edge_editation_highlights = true;
311 this._cy.on('handlemouseout', () => {
312 if (this.zoneTagMode) {
313 this.zoneTagMode = this.zones[this.activeZoneInstance.type].getTagModeId();
316 if (this._cy.scratch('_edge_editation_highlights') === true) {
317 this._cy.removeScratch('_edge_editation_highlights');
318 this._cy.emit('hidehandles');
319 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
323 this._cy.on('tapend', (event: Cy.EventObject) => {
324 this.onTapEnd(event);
327 this._cy.on('boxselect', 'node', (event: Cy.EventObject) => {
328 this.unsetActiveZoneInstance();
329 this.selectComponentInstance(event.cyTarget.data().componentInstance);
332 this._cy.on('canvasredraw', (event: Cy.EventObject) => {
333 if (this.zoneTagMode) {
334 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, this.activeZoneInstance);
338 this._cy.on('handletagclick', (event: Cy.EventObject, eventData: any) => {
339 this.compositionGraphZoneUtils.handleTagClick(this._cy, this.activeZoneInstance, eventData.nodeId);
343 private initViewMode() {
345 if (this.isViewOnly()) {
346 // remove event listeners
347 this._cy.off('drag');
348 this._cy.off('handlemouseout');
349 this._cy.off('handlemouseover');
350 this._cy.off('canvasredraw');
351 this._cy.off('handletagclick');
352 this._cy.edges().unselectify();
356 private saveChangedCapabilityProperties = (): Promise<PropertyBEModel[]> => {
357 return new Promise<PropertyBEModel[]>((resolve) => {
358 const capabilityPropertiesBE: PropertyBEModel[] = this.connectionWizardService.changedCapabilityProperties.map((prop) => {
359 prop.value = prop.getJSONValue();
360 const propBE = new PropertyBEModel(prop);
361 propBE.parentUniqueId = this.connectionWizardService.selectedMatch.relationship.relation.capabilityOwnerId;
364 if (capabilityPropertiesBE.length > 0) {
365 // if there are capability properties to update, then first update capability properties and then resolve promise
366 this.componentInstanceService
367 .updateInstanceCapabilityProperties(
368 this.topologyTemplate,
369 this.connectionWizardService.selectedMatch.toNode,
370 this.connectionWizardService.selectedMatch.capability,
371 capabilityPropertiesBE
373 .subscribe((response) => {
374 console.log('Update resource instance capability properties response: ', response);
375 this.connectionWizardService.changedCapabilityProperties = [];
376 resolve(capabilityPropertiesBE);
379 // no capability properties to update, immediately resolve promise
380 resolve(capabilityPropertiesBE);
385 private loadCompositionData = () => {
386 this.loaderService.activate();
387 this.topologyTemplateService.getComponentCompositionData(this.topologyTemplateId, this.topologyTemplateType).subscribe((response: ComponentGenericResponse) => {
388 if (this.topologyTemplateType === ComponentType.SERVICE) {
389 this.compositionService.forwardingPaths = (response as ServiceGenericResponse).forwardingPaths;
391 this.compositionService.componentInstances = response.componentInstances;
392 this.compositionService.componentInstancesRelations = response.componentInstancesRelations;
393 this.compositionService.groupInstances = response.groupInstances;
394 this.compositionService.policies = response.policies;
395 this.loadGraphData();
396 this.loaderService.deactivate();
397 }, (error) => { this.loaderService.deactivate(); });
400 private loadGraph = () => {
401 const graphEl = this.elRef.nativeElement.querySelector('.sdc-composition-graph-wrapper');
402 this.initGraph(graphEl);
403 this.zones = this.compositionGraphZoneUtils.createCompositionZones();
404 this.registerCytoscapeGraphEvents();
405 this.registerCustomEvents();
409 private initGraphNodes() {
412 const handles = new CytoscapeEdgeEditation();
413 handles.init(this._cy);
414 if (!this.isViewOnly()) { // Init nodes handle extension - enable dynamic links
415 handles.initNodeEvents();
416 handles.registerHandle(ComponentInstanceNodesStyle.getAddEdgeHandle());
418 handles.registerHandle(ComponentInstanceNodesStyle.getTagHandle());
419 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedPolicyHandle());
420 handles.registerHandle(ComponentInstanceNodesStyle.getTaggedGroupHandle());
423 _.each(this.compositionService.componentInstances, (instance) => {
424 const compositionGraphNode: CompositionCiNodeBase = this.nodesFactory.createNode(instance);
425 this.commonGraphUtils.addComponentInstanceNodeToGraph(this._cy, compositionGraphNode);
430 private loadGraphData = () => {
431 this.initGraphNodes();
432 this.compositionGraphLinkUtils.initGraphLinks(this._cy, this.compositionService.componentInstancesRelations);
433 this.compositionGraphZoneUtils.initZoneInstances(this.zones);
434 setTimeout(() => { // Need setTimeout so that angular canvas changes will take effect before resize & center
435 this.generalGraphUtils.zoomAllWithMax(this._cy, 1);
437 this.componentInstanceNames = _.map(this._cy.nodes(), (node) => node.data('name'));
440 private initGraph(graphEl: JQuery) {
442 this._cy = cytoscape({
444 style: ComponentInstanceNodesStyle.getCompositionGraphStyle(),
445 zoomingEnabled: true,
448 userZoomingEnabled: false,
449 userPanningEnabled: true,
450 selectionType: 'single',
451 boxSelectionEnabled: true,
452 autolock: this.isViewOnly(),
453 autoungrabify: this.isViewOnly()
456 // Testing Bridge that allows Cypress tests to select a component on canvas not via DOM
457 if (window.Cypress) {
458 window.testBridge = this.createCanvasTestBridge();
462 private createCanvasTestBridge(): any {
464 selectComponentInstance: (componentName: string) => {
465 const matchingNodesByName = this.nodesGraphUtils.getMatchingNodesByName(this._cy, componentName);
466 const component = new ComponentInstance(matchingNodesByName.first().data().componentInstance);
467 this.selectComponentInstance(component);
472 // -------------------------------------------- ZONES---------------------------------------------------------//
473 private zoneMinimizeToggle = (zoneType: ZoneInstanceType): void => {
474 this.zones[zoneType].minimized = !this.zones[zoneType].minimized;
477 private zoneInstanceModeChanged = (newMode: ZoneInstanceMode, instance: ZoneInstance, zoneId: ZoneInstanceType): void => {
478 if (this.zoneTagMode) { // we're in tag mode.
479 if (instance === this.activeZoneInstance && newMode === ZoneInstanceMode.NONE) { // we want to turn tag mode off.
480 this.zoneTagMode = null;
481 this.activeZoneInstance.mode = ZoneInstanceMode.SELECTED;
482 this.compositionGraphZoneUtils.endCyTagMode(this._cy);
483 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_END, instance);
487 // when active zone instance gets hover/none, don't actually change mode, just show/hide indications
488 if (instance !== this.activeZoneInstance || (instance === this.activeZoneInstance && newMode > ZoneInstanceMode.HOVER)) {
489 instance.mode = newMode;
492 if (newMode === ZoneInstanceMode.NONE) {
493 this.compositionGraphZoneUtils.hideZoneTagIndications(this._cy);
494 if (this.zones[ZoneInstanceType.GROUP]) {
495 this.compositionGraphZoneUtils.hideGroupZoneIndications(this.zones[ZoneInstanceType.GROUP].instances);
498 if (newMode >= ZoneInstanceMode.HOVER) {
499 this.compositionGraphZoneUtils.showZoneTagIndications(this._cy, instance);
500 if (instance.type === ZoneInstanceType.POLICY && this.zones[ZoneInstanceType.GROUP]) {
501 this.compositionGraphZoneUtils.showGroupZoneIndications(this.zones[ZoneInstanceType.GROUP].instances, instance);
504 if (newMode >= ZoneInstanceMode.SELECTED) {
505 this._cy.$('node:selected').unselect();
506 if (this.activeZoneInstance && this.activeZoneInstance !== instance && newMode >= ZoneInstanceMode.SELECTED) {
507 this.activeZoneInstance.mode = ZoneInstanceMode.NONE;
509 this.activeZoneInstance = instance;
510 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_ZONE_INSTANCE_SELECTED, instance);
511 this.store.dispatch(new SetSelectedComponentAction({
512 component: instance.instanceData,
513 type: SelectedComponentType[ZoneInstanceType[instance.type]]
516 if (newMode === ZoneInstanceMode.TAG) {
517 this.compositionGraphZoneUtils.startCyTagMode(this._cy);
518 this.zoneTagMode = this.zones[zoneId].getTagModeId();
519 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_CANVAS_TAG_START, zoneId);
524 private zoneInstanceTagged = (taggedInstance: ZoneInstance) => {
525 this.activeZoneInstance.addOrRemoveAssignment(taggedInstance.instanceData.uniqueId, ZoneInstanceAssignmentType.GROUPS);
526 const newHandle: string = this.compositionGraphZoneUtils.getCorrectHandleForNode(taggedInstance.instanceData.uniqueId, this.activeZoneInstance);
527 taggedInstance.showHandle(newHandle);
530 private unsetActiveZoneInstance = (): void => {
531 if (this.activeZoneInstance) {
532 this.activeZoneInstance.mode = ZoneInstanceMode.NONE;
533 this.activeZoneInstance = null;
534 this.zoneTagMode = null;
538 private selectComponentInstance = (componentInstance: ComponentInstance) => {
539 this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_NODE_SELECTED, componentInstance);
540 this.store.dispatch(new SetSelectedComponentAction({
541 component: componentInstance,
542 type: SelectedComponentType.COMPONENT_INSTANCE
546 private selectTopologyTemplate = () => {
547 this.store.dispatch(new SetSelectedComponentAction({
548 component: this.topologyTemplate,
549 type: SelectedComponentType.TOPOLOGY_TEMPLATE
553 private zoneBackgroundClicked = (): void => {
554 if (!this.zoneTagMode && this.activeZoneInstance) {
555 this.unsetActiveZoneInstance();
556 this.selectTopologyTemplate();
560 private zoneAssignmentSaveStart = () => {
561 this.loaderService.activate();
564 private zoneAssignmentSaveComplete = (success: boolean) => {
565 this.loaderService.deactivate();
567 this.notificationService.push(new NotificationSettings('error', 'Update Failed', 'Error'));
571 private deleteZoneInstance = (deletedInstance: UIZoneInstanceObject) => {
572 if (deletedInstance.type === ZoneInstanceType.POLICY) {
573 this.compositionService.policies = this.compositionService.policies.filter((policy) => policy.uniqueId !== deletedInstance.uniqueId);
574 } else if (deletedInstance.type === ZoneInstanceType.GROUP) {
575 this.compositionService.groupInstances = this.compositionService.groupInstances.filter((group) => group.uniqueId !== deletedInstance.uniqueId);
577 // remove it from zones
578 this.zones[deletedInstance.type].removeInstance(deletedInstance.uniqueId);
579 if (deletedInstance.type === ZoneInstanceType.GROUP && !_.isEmpty(this.zones[ZoneInstanceType.POLICY])) {
580 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(deletedInstance.uniqueId, [this.zones[ZoneInstanceType.POLICY]], ZoneInstanceAssignmentType.GROUPS);
582 this.selectTopologyTemplate();
584 // -------------------------------------------------------------------------------------------------------------//
586 private registerCustomEvents() {
588 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_GROUP_INSTANCE_UPDATE, (groupInstance: GroupInstance) => {
589 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(this.zones, groupInstance);
590 this.notificationService.push(new NotificationSettings('success', 'Group Updated', 'Success'));
593 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_POLICY_INSTANCE_UPDATE, (policyInstance: PolicyInstance) => {
594 this.compositionGraphZoneUtils.findAndUpdateZoneInstanceData(this.zones, policyInstance);
595 this.notificationService.push(new NotificationSettings('success', 'Policy Updated', 'Success'));
598 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_IN, (leftPaletteComponent: LeftPaletteComponent) => {
599 this.compositionGraphPaletteUtils.onComponentHoverIn(leftPaletteComponent, this._cy);
602 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_ADD_ZONE_INSTANCE_FROM_PALETTE,
603 (component: TopologyTemplate, paletteComponent: LeftPaletteComponent, startPosition: Point) => {
605 const zoneType: ZoneInstanceType = this.compositionGraphZoneUtils.getZoneTypeForPaletteComponent(paletteComponent.categoryType);
606 this.compositionGraphZoneUtils.showZone(this.zones[zoneType]);
608 this.loaderService.activate();
609 this.compositionGraphZoneUtils.createZoneInstanceFromLeftPalette(zoneType, paletteComponent.type).subscribe((zoneInstance: ZoneInstance) => {
610 this.loaderService.deactivate();
611 this.compositionGraphZoneUtils.addInstanceToZone(this.zones[zoneInstance.type], zoneInstance, true);
612 this.compositionGraphZoneUtils.createPaletteToZoneAnimation(startPosition, zoneType, zoneInstance);
614 this.loaderService.deactivate();
618 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_HOVER_OUT, () => {
619 this._cy.emit('hidehandles');
620 this.matchCapabilitiesRequirementsUtils.resetFadedNodes(this._cy);
623 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_START, (dragElement, dragComponent) => {
624 this.dragElement = dragElement;
625 this.dragComponent = ComponentInstanceFactory.createComponentInstanceFromComponent(dragComponent, this.workspaceService.metadata.categories[0].useServiceSubstitutionForNestedServices);
628 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DRAG_ACTION, (position: Point) => {
629 const draggedElement = document.getElementById('draggable_element');
630 draggedElement.className = this.compositionGraphPaletteUtils.isDragValid(this._cy, position) ? 'valid-drag' : 'invalid-drag';
633 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_PALETTE_COMPONENT_DROP, (event: DndDropEvent) => {
637 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_NAME_CHANGED, (component: ComponentInstance) => {
638 const selectedNode = this._cy.getElementById(component.uniqueId);
639 selectedNode.data().componentInstance.name = component.name;
640 selectedNode.data('name', component.name); // used for tooltip
641 selectedNode.data('displayName', selectedNode.data().getDisplayName()); // abbreviated
644 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, (componentInstanceId: string) => {
645 const nodeToDelete = this._cy.getElementById(componentInstanceId);
646 this.nodesGraphUtils.deleteNode(this._cy, this.topologyTemplate, nodeToDelete);
647 this.selectTopologyTemplate();
650 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_ZONE_INSTANCE, (deletedInstance: UIZoneInstanceObject) => {
651 this.deleteZoneInstance(deletedInstance);
654 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE_SUCCESS, (componentInstanceId: string) => {
655 if (!_.isEmpty(this.zones)) {
656 this.compositionGraphZoneUtils.updateTargetsOrMembersOnCanvasDelete(componentInstanceId, this.zones, ZoneInstanceAssignmentType.COMPONENT_INSTANCES);
660 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_EDGE, (releaseLoading: boolean, linksToDelete: Cy.CollectionEdges) => {
661 this.compositionGraphLinkUtils.deleteLink(this._cy, this.topologyTemplate, releaseLoading, linksToDelete);
664 this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_VERSION_CHANGED, (component: ComponentInstance) => {
665 // Remove everything from graph and reload it all
666 this._cy.elements().remove();
667 this.loadCompositionData();
668 setTimeout(() => { this._cy.getElementById(component.uniqueId).select(); }, 1000);
669 this.selectComponentInstance(component);
671 this.eventListenerService.registerObserverCallback(DEPENDENCY_EVENTS.ON_DEPENDENCY_CHANGE, (ischecked: boolean) => {
673 this._cy.$('node:selected').addClass('dependent');
675 // due to defect in cytoscape, just changing the class does not replace the icon, and i need to revert to original icon with no markings.
676 this._cy.$('node:selected').removeClass('dependent');
677 this._cy.$('node:selected').style({'background-image': this._cy.$('node:selected').data('originalImg')});
681 private createLinkFromMenu = (): void => {
682 this.saveChangedCapabilityProperties().then(() => {
683 this.compositionGraphLinkUtils.createLinkFromMenu(this._cy, this.connectionWizardService.selectedMatch);
687 private deleteRelation = (link: Cy.CollectionEdges) => {
688 // if multiple edges selected, delete the VL itself so edges get deleted automatically
689 if (this._cy.$('edge:selected').length > 1) {
690 this.nodesGraphUtils.deleteNode(this._cy, this.topologyTemplate, this._cy.$('node:selected'));
692 this.compositionGraphLinkUtils.deleteLink(this._cy, this.topologyTemplate, true, link);
696 private viewRelation = (link: Cy.CollectionEdges) => {
698 const linkData = link.data();
699 const sourceNode: CompositionCiNodeBase = link.source().data();
700 const targetNode: CompositionCiNodeBase = link.target().data();
701 const relationship: Relationship = linkData.relation.relationships[0];
703 this.compositionGraphLinkUtils.getRelationRequirementCapability(relationship, sourceNode.componentInstance, targetNode.componentInstance).then((objReqCap) => {
704 const capability = objReqCap.capability;
705 const requirement = objReqCap.requirement;
707 this.connectionWizardService.connectRelationModel = new ConnectRelationModel(sourceNode, targetNode, []);
708 this.connectionWizardService.selectedMatch = new Match(requirement, capability, true, linkData.source, linkData.target);
709 this.connectionWizardService.selectedMatch.relationship = relationship;
711 const title = `Connection Properties`;
712 const saveButton: ButtonModel = new ButtonModel('Save', 'blue', () => {
713 this.saveChangedCapabilityProperties().then(() => {
714 this.modalService.closeCurrentModal();
717 const cancelButton: ButtonModel = new ButtonModel('Cancel', 'white', () => {
718 this.modalService.closeCurrentModal();
720 const modal = new ModalModel('xl', title, '', [saveButton, cancelButton]);
721 const modalInstance = this.modalService.createCustomModal(modal);
722 this.modalService.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
723 modalInstance.instance.open();
725 new Promise((resolve) => {
726 if (!this.connectionWizardService.selectedMatch.capability.properties) {
727 this.componentInstanceService.getInstanceCapabilityProperties(this.topologyTemplateType, this.topologyTemplateId, linkData.target, capability)
730 }, () => { /* do nothing */ });
735 this.modalService.addDynamicContentToModal(modalInstance, ConnectionPropertiesViewComponent);
737 }, () => { /* do nothing */ });
740 private openModifyLinkMenu = (linkMenuObject: LinkMenu, $event) => {
742 const menuConfig: menuItem[] = [{
746 iconMode: 'secondary',
749 action: () => this.viewRelation(linkMenuObject.link as Cy.CollectionEdges)
752 if (!this.isViewOnly()) {
757 iconMode: 'secondary',
760 action: () => this.deleteRelation(linkMenuObject.link as Cy.CollectionEdges)
763 this.simplePopupMenuService.openBaseMenu(menuConfig, {
764 x: $event.originalEvent.x,
765 y: $event.originalEvent.y