2 Copyright 2020 TATA ELXSI
4 Licensed under the Apache License, Version 2.0 (the 'License');
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
16 Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
19 * @file NS Compose Component
21 import { isNullOrUndefined } from 'util';
22 import { HttpHeaders } from '@angular/common/http';
23 import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core';
24 import { ActivatedRoute, Router } from '@angular/router';
25 import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
26 import { TranslateService } from '@ngx-translate/core';
27 import { NotifierService } from 'angular-notifier';
28 import { APIURLHEADER, CONSTANTNUMBER, ERRORDATA, MODALCLOSERESPONSEDATA, MODALCLOSERESPONSEWITHCP } from 'CommonModel';
29 import { ConfirmationTopologyComponent } from 'ConfirmationTopology';
30 import * as d3 from 'd3';
31 import { DataService } from 'DataService';
32 import { environment } from 'environment';
33 import * as HttpStatus from 'http-status-codes';
34 import * as jsyaml from 'js-yaml';
43 Tick, TickPath, VLC, VLD, VNFPROFILE
45 import { RestService } from 'RestService';
46 import { SharedService } from 'SharedService';
47 import { VNFD, VNFData } from 'VNFDModel';
51 * @Component takes NSComposerComponent.html as template url
54 selector: 'app-ns-composer',
55 templateUrl: './NSComposerComponent.html',
56 styleUrls: ['./NSComposerComponent.scss'],
57 encapsulation: ViewEncapsulation.None
59 /** Exporting a class @exports NSComposerComponent */
60 export class NSComposerComponent {
61 /** To inject services @public */
62 public injector: Injector;
63 /** View child contains graphContainer ref @public */
64 @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef;
65 /** dataService to pass the data from one component to another @public */
66 public dataService: DataService;
67 /** Contains VNFD Informations @public */
68 public nsPackageDetails: NSData = { id: '', name: '', description: '', version: '', designer: '' };
69 /** Contains VL Details @public */
70 public virtualLinkDesc: VLD = {
74 /** Contains the information of the type of modification @public */
75 public putType: string;
76 /** Contains index of old VLD @public */
77 public getOldVLDIndex: string[];
78 /** Conatins mousedown action @public */
79 public mousedownNode: COMPOSERNODES = null;
80 /** Conatins mouseup action @public */
81 public mouseupNode: COMPOSERNODES = null;
82 /** Conatins mousedownLink action @public */
83 public mousedownLink: COMPOSERNODES = null;
84 /** Conatins current Selection node action @public */
85 public currentSelectedNode: COMPOSERNODES = null;
86 /** Conatins current Selection node action @public */
87 public currentSelectedLink: COMPOSERNODES = null;
88 /** Need to show the NSD Details @public */
89 public isShowNSDDetails: boolean = true;
90 /** Contains the node information of VL @public */
91 public vlNodes: {}[] = [];
92 /** Need to show the VL Details @public */
93 public isShowVLDetails: boolean = false;
94 /** Contains the node information of VNF @public */
95 public vnfNodes: {}[] = [];
96 /** contains the VNF Details @public */
97 public vnfData: VNFPROFILE;
98 /** contains the Virtual Link connectivity Details @public */
99 public virtualLinkProfileID: string;
100 /** Need to show the VNF Details @public */
101 public isShowVNFDetails: boolean = false;
102 /** Contains the node information of CP @public */
103 public cpNodes: {}[] = [];
104 /** Need to show the CP Details */
106 /** Need to show the VNF Details @public */
107 public isShowCPDetails: boolean = false;
108 /** random number count @public */
109 public randomNumberLength: number;
110 /** Contains the vnfd information @public */
111 public vnfList: VNFD[] = [];
112 /** Add the activeclass for the selected @public */
113 public activeClass: string = 'active';
114 /** Add the fixed class for the freeze @public */
115 public fixedClass: string = 'fixed';
116 /** Check the loading results @public */
117 public isLoadingResults: boolean = true;
118 /** Give the message for the loading @public */
119 public message: string = 'PLEASEWAIT';
120 /** Get VNF selected node @public */
121 public getVNFSelectedData: VNFD;
122 /** Assign the forcesimulation active @public */
123 public forceSimulationActive: boolean = false;
124 /** Assign pinned class for the button when freezed @public */
125 public classApplied: boolean = false;
126 /** Contains sidebar open status @public */
127 public sideBarOpened: boolean = false;
128 /** Contains SVG attributes @private */
129 // eslint-disable-next-line @typescript-eslint/no-explicit-any
131 /** Contains the Drag line */
132 // eslint-disable-next-line @typescript-eslint/no-explicit-any
133 private dragLine: any;
134 /** Contains VL node @private */
135 // eslint-disable-next-line @typescript-eslint/no-explicit-any
137 /** Contains VNFD node @private */
138 // eslint-disable-next-line @typescript-eslint/no-explicit-any
139 private vnfdnode: any;
140 /** Contains CP node @private */
141 // eslint-disable-next-line @typescript-eslint/no-explicit-any
143 /** Rendered nodes represent VL @private */
144 // eslint-disable-next-line @typescript-eslint/no-explicit-any
145 private gvlNode: any;
146 /** Rendered nodes represent VL @private */
147 // eslint-disable-next-line @typescript-eslint/no-explicit-any
148 private gvnfdNode: any;
149 /** Rendered nodes represent VL @private */
150 // eslint-disable-next-line @typescript-eslint/no-explicit-any
151 private gcpNode: any;
152 /** Contains forced node animations @private */
153 // eslint-disable-next-line @typescript-eslint/no-explicit-any
155 /** Contains all the selected node @private */
156 private selectedNode: COMPOSERNODES[] = [];
157 /** Contains the connected point @private */
158 private connectionPoint: string;
159 /** Contains id of the node @private */
160 private identifier: string;
161 /** Contains copy of NSD information @private */
162 private nsdCopy: string;
163 /** Contains the VNFD copy @private */
164 private vnfdCopy: string;
165 /** Contains path information of the node */
166 // eslint-disable-next-line @typescript-eslint/no-explicit-any
168 /** Contains the node information @private */
169 private nodes: COMPOSERNODES[] = [];
170 /** Contains the link information of nodes @private */
171 private links: {}[] = [];
172 /** Contains the NS information @private */
173 private nsData: NSDDetails;
174 /** Instance of the rest service @private */
175 private restService: RestService;
176 /** Service holds the router information @private */
177 private router: Router;
178 /** Holds teh instance of AuthService class of type AuthService @private */
179 private activatedRoute: ActivatedRoute;
180 /** Notifier service to popup notification @private */
181 private notifierService: NotifierService;
182 /** Controls the header form @private */
183 private headers: HttpHeaders;
184 /** Contains tranlsate instance @private */
185 private translateService: TranslateService;
186 /** Contains lastkeypressed instance @private */
187 private lastKeyDown: number = -1;
188 /** Instance of the modal service @private */
189 private modalService: NgbModal;
190 /** Setting the Value of connection point refrence of the CP @private */
191 private setVnfdConnectionPointRef: string;
192 /** Setting the Value of VL name for confirmation @private */
193 private vlName: string;
194 /** Setting the Value of VNFD name for confirmation @private */
195 private setVnfdName: string;
196 /** Contains all methods related to shared @private */
197 private sharedService: SharedService;
198 /** Contains selected node VNF profile objects @private */
199 private selectedVNFProfile: VNFPROFILE[];
201 constructor(injector: Injector) {
202 this.injector = injector;
203 this.restService = this.injector.get(RestService);
204 this.dataService = this.injector.get(DataService);
205 this.router = this.injector.get(Router);
206 this.activatedRoute = this.injector.get(ActivatedRoute);
207 this.notifierService = this.injector.get(NotifierService);
208 this.translateService = this.injector.get(TranslateService);
209 this.modalService = this.injector.get(NgbModal);
210 this.sharedService = this.injector.get(SharedService);
212 /** Lifecyle Hooks the trigger before component is instantiated @public */
213 public ngOnInit(): void {
214 this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
216 this.headers = new HttpHeaders({
217 'Content-Type': 'application/gzip',
218 Accept: 'application/json',
219 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
222 /** Events handles at drag on D3 region @public */
223 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
224 public drag(ev: any): void {
225 if (ev.target.id === 'vl') {
226 ev.dataTransfer.setData('text', ev.target.id);
228 ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
231 /** On clicking redirect to NS edit page @public */
232 public onEdit(): void {
233 this.router.navigate(['/packages/ns/edit/', this.identifier]).catch((): void => {
234 // Catch Navigation Error
237 /** Events handles drop at D3 region @public */
238 public drop(ev: DragEvent): void {
240 const getDropedName: string = ev.dataTransfer.getData('text');
241 if (getDropedName === 'vl') {
242 this.svg.selectAll('*').remove();
243 this.vldropComposer();
245 this.svg.selectAll('*').remove();
246 const vnfdName: string = ev.dataTransfer.getData('text');
247 this.vnfdropComposer(vnfdName);
250 /** Drop VL Composer Data @public */
251 public vldropComposer(): void {
252 this.randomNumberLength = CONSTANTNUMBER.randomNumber;
253 const generateId: string = 'ns_vl_' + this.sharedService.randomString();
254 if (this.nsData['virtual-link-desc'] === undefined) {
255 this.nsData['virtual-link-desc'] = [];
257 this.nsData['virtual-link-desc'].push({
259 'mgmt-network': false
261 this.putType = 'nsdadd';
262 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
264 /** Drop VNFD Composer Data @public */
265 public vnfdropComposer(vnfdName: string): void {
266 const vnfID: string = 'ns_vnfd_' + this.sharedService.randomString();
267 if (this.nsData.df.length > 0) {
268 this.addVNFDID(vnfdName);
269 this.nsData.df.forEach((res: DF): void => {
270 if (res['vnf-profile'] === undefined) {
271 res['vnf-profile'] = [];
273 res['vnf-profile'].push({
275 'virtual-link-connectivity': [],
280 Object.assign(this.nsData.df, {
284 'virtual-link-connectivity': [],
289 this.putType = 'vnfdadd';
290 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
292 /** Add the VNFD-ID while drop VNFD if not exists @public */
293 public addVNFDID(vnfdName: string): void {
294 const vnfdIDArray: string[] = this.nsData['vnfd-id'];
295 if (vnfdIDArray !== undefined) {
296 if (vnfdIDArray.indexOf(vnfdName) === -1) {
297 vnfdIDArray.push(vnfdName);
300 Object.assign(this.nsData, {
301 'vnfd-id': [vnfdName]
305 /** Events handles allow drop on D3 region @public */
306 public allowDrop(ev: DragEvent): void {
309 /** Save NSD Information @public */
310 public saveNSD(): void {
311 if (this.nsPackageDetails.id !== undefined) {
312 this.nsData.id = this.nsPackageDetails.id;
314 if (this.nsPackageDetails.name !== undefined) {
315 this.nsData.name = this.nsPackageDetails.name;
317 if (this.nsPackageDetails.description !== undefined) {
318 this.nsData.description = this.nsPackageDetails.description;
320 if (this.nsPackageDetails.version !== undefined) {
321 this.nsData.version = this.nsPackageDetails.version;
323 if (this.nsPackageDetails.designer !== undefined) {
324 this.nsData.designer = this.nsPackageDetails.designer;
326 this.putType = 'nsdUpdate';
327 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
329 /** Save Virtual Link @public */
330 public saveVL(vlid: string): void {
331 this.nsData['virtual-link-desc'].forEach((result: VLD): void => {
332 if (result.id === vlid) {
333 result['mgmt-network'] = !isNullOrUndefined(this.virtualLinkDesc['mgmt-network']) ? this.virtualLinkDesc['mgmt-network'] : true;
336 this.putType = 'vlUpdate';
337 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
339 /** Add the new Data @public */
340 public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void {
341 this.isLoadingResults = true;
342 let successMessage: string = '';
343 if (putType === 'nsdadd') {
344 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD';
345 } else if (putType === 'vnfdadd') {
346 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD';
347 } else if (putType === 'cpAdded') {
348 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS';
349 } else if (putType === 'nsdUpdate') {
350 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
351 } else if (putType === 'vlUpdate') {
352 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
353 } else if (putType === 'nsddelete') {
354 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD';
355 } else if (putType === 'vnfddelete') {
356 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD';
357 } else if (putType === 'nsdelete') {
358 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS';
359 } else if (putType === 'linkdelete') {
360 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK';
362 /** Below hide for conflicts with light weight UI */
363 const apiURLHeader: APIURLHEADER = {
364 url: apiURL + '/' + identifier + '/nsd_content',
365 httpOptions: { headers: this.headers }
367 const nsData: NSDATACREATION = { nsd: { nsd: [] } };
369 nsData.nsd.nsd.push(data);
370 const descriptorInfo: string = jsyaml.dump(nsData, { sortKeys: true });
371 this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo })
372 .then((content: ArrayBuffer): void => {
373 this.restService.putResource(apiURLHeader, content).subscribe((res: {}): void => {
375 this.notifierService.notify('success', this.translateService.instant(successMessage));
376 this.isLoadingResults = false;
377 }, (error: ERRORDATA): void => {
379 this.restService.handleError(error, 'put');
380 this.isLoadingResults = false;
382 }).catch((): void => {
383 this.notifierService.notify('error', this.translateService.instant('ERROR'));
384 this.isLoadingResults = false;
387 /** Show Info @public */
388 public showInfo(): void {
389 // eslint-disable-next-line security/detect-non-literal-fs-filename
390 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
391 modalRef.componentInstance.topologyType = 'Info';
392 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
393 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
397 }).catch((): void => {
398 // Catch Navigation Error
401 /** Event to freeze the animation @public */
402 public onFreeze(): void {
403 this.classApplied = !this.classApplied;
404 const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
405 d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
406 if (alreadyFixedIsActive) {
409 this.forceSimulationActive = alreadyFixedIsActive;
410 this.nodes.forEach((d: COMPOSERNODES): void => {
411 d.fx = (alreadyFixedIsActive) ? null : d.x;
412 d.fy = (alreadyFixedIsActive) ? null : d.y;
414 if (alreadyFixedIsActive) {
415 this.force.restart();
418 /** Events handles when dragended @public */
419 public toggleSidebar(): void {
420 this.sideBarOpened = !this.sideBarOpened;
421 this.deselectAllNodes();
422 this.showRightSideInfo(true, false, false, false);
424 /** Prepare information for node creation of VNFD @private */
425 private generateData(): void {
426 this.generateVNFData();
427 this.generateDataNSDTopology();
428 this.sideBarOpened = false;
430 /** Prepare the information of the VNFD @private */
431 private generateVNFData(): void {
432 this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFD[]): void => {
433 this.vnfList = vnfdPackageData;
434 }, (error: ERRORDATA): void => {
435 this.restService.handleError(error, 'get');
438 /** Prepare information for node creation of NSD Topology @private */
439 private generateDataNSDTopology(): void {
442 this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails): void => {
443 delete nsData._admin;
445 delete nsData._links;
446 this.nsData = nsData;
447 this.generateNSInfo(nsData);
448 if (nsData['virtual-link-desc'] !== undefined) {
449 /** Details of the VL */
450 this.nsDataVLD(nsData);
452 if (this.nsData.df.length > 0) {
453 this.nsData.df.forEach((res: DF): void => {
454 if (res['vnf-profile'] !== undefined) {
455 /** Details of the VNFD */
456 this.nsDataConstituentVNFD(nsData);
460 if (nsData.df.length > 0) {
461 this.nsDataVLDLinkCreation(nsData);
463 this.separateAndCreatenode();
464 }, (error: ERRORDATA): void => {
465 if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
466 this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
467 // Catch Navigation Error
470 this.restService.handleError(error, 'get');
472 this.isLoadingResults = false;
473 this.isShowNSDDetails = false;
476 /** Generate the NS Package Information */
477 private generateNSInfo(nsData: NSDDetails): void {
478 this.nsPackageDetails.id = nsData.id;
479 this.nsPackageDetails.name = nsData.name;
480 this.nsPackageDetails.description = nsData.description;
481 this.nsPackageDetails.version = nsData.version;
482 this.nsPackageDetails.designer = nsData.designer;
484 /** nsData VL node creation function @private */
485 private nsDataVLD(nsData: NSDDetails): void {
486 nsData['virtual-link-desc'].forEach((res: VLD): void => {
487 this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
490 /** nsData VNFD node creation function @private */
491 private nsDataConstituentVNFD(nsData: NSDDetails): void {
492 nsData.df.forEach((resDF: DF): void => {
493 resDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
496 id: resVNF['vnfd-id'] + ':' + resVNF.id,
499 name: resVNF['vnfd-id'],
500 nodeIndex: resVNF.id,
501 selectorId: resVNF['vnfd-id'] + '_' + resVNF.id
503 if (resVNF['virtual-link-connectivity'] !== undefined) {
504 this.nsDataCP(resVNF, resVNF.id);
509 /** nsData CP node creation function @private */
510 private nsDataCP(resVNF: VNFPROFILE, vnfID: string): void {
511 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
512 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
515 id: vnfID + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'],
518 name: resultCCI['constituent-cpd-id'],
519 nodeIndex: resultCCI['constituent-base-element-id'],
520 selectorId: 'osm-' + resultCCI['constituent-cpd-id'] + '-' + vnfID + resultCCI['constituent-base-element-id'] + index
525 /** nsData Link node creation function @private */
526 private nsDataVLDLinkCreation(nsData: NSDDetails): void {
527 nsData.df.forEach((resDF: DF): void => {
528 if (resDF['vnf-profile'] !== undefined) {
529 resDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
530 this.nsdCopy = resVNF['vnfd-id'] + ':' + resVNF.id;
531 if (resVNF['virtual-link-connectivity'] !== undefined) {
532 this.nsDataVNFDConnectionPointRefrence(resVNF);
538 /** nsDataVNFDConnectionPointRefrence undefined Call this function @private */
539 private nsDataVNFDConnectionPointRefrence(resVNF: VNFPROFILE): void {
540 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
541 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
542 this.vnfdCopy = resultVLC['virtual-link-profile-id'];
543 this.connectionPoint = resVNF.id + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'];
544 const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES): string => e.id).indexOf(this.connectionPoint);
545 const nsdPos: number = this.nodes.map((e: COMPOSERNODES): string => e.id).indexOf(this.nsdCopy);
546 const vnfdPos: number = this.nodes.map((e: COMPOSERNODES): string => e.id).indexOf(this.vnfdCopy);
549 // eslint-disable-next-line security/detect-object-injection
550 source: this.nodes[connectionPointPos],
551 // eslint-disable-next-line security/detect-object-injection
552 target: this.nodes[nsdPos]
555 // eslint-disable-next-line security/detect-object-injection
556 source: this.nodes[connectionPointPos],
557 // eslint-disable-next-line security/detect-object-injection
558 target: this.nodes[vnfdPos]
563 /** Separate and create node @private */
564 private separateAndCreatenode(): void {
565 this.seprateNodes(this.nodes);
566 this.createnode(this.nodes);
567 this.isLoadingResults = false;
569 /** Get the default Configuration of containers @private */
570 private getGraphContainerAttr(): GRAPHDETAILS {
583 sourcePaddingYes: 17,
593 /** Separate the nodes along with its tyep @private */
594 private seprateNodes(node: COMPOSERNODES[]): void {
595 this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
596 node.forEach((nodeList: COMPOSERNODES): void => {
597 if (nodeList.type === 'vld') {
598 this.vlNodes.push(nodeList);
599 } else if (nodeList.type === 'vnfd') {
600 this.vnfNodes.push(nodeList);
601 } else if (nodeList.type === 'ns') {
602 this.cpNodes.push(nodeList);
606 /** Node is created and render at D3 region @private */
607 private createnode(node: COMPOSERNODES[]): void {
608 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
609 d3.selectAll('svg#graphContainer > *').remove();
610 d3.select(window).on('keydown', (): void => { this.keyDown(); });
611 d3.select(window).on('keyup', (): void => { this.keyUp(); });
612 this.svg = d3.select('#graphContainer')
613 .attr('oncontextmenu', 'return false;')
614 .attr('viewBox', `0 0 ${graphContainerAttr.width} ${graphContainerAttr.height}`)
615 .on('mousemove', (): void => { this.mousemove(); });
616 this.force = d3.forceSimulation()
617 .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
618 .force('link', d3.forceLink().id((d: TickPath): string => d.id).distance(graphContainerAttr.distance))
619 .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
620 graphContainerAttr.height / graphContainerAttr.forcey))
621 .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
622 .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
623 .on('tick', (): void => { this.tick(); });
624 this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
625 this.path = this.svg.append('svg:g').selectAll('path');
626 this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
627 this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
628 this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
632 /** update force layout (called automatically each iteration) @private */
633 private tick(): void {
634 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
635 // draw directed edges with proper padding from node centers
636 this.path.attr('class', 'link').attr('d', (d: Tick): string => {
637 const deltaX: number = d.target.x - d.source.x;
638 const deltaY: number = d.target.y - d.source.y;
639 const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
640 const normX: number = deltaX / dist;
641 const normY: number = deltaY / dist;
642 const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
643 const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
644 const sourceX: number = d.source.x + (sourcePadding * normX);
645 const sourceY: number = d.source.y + (sourcePadding * normY);
646 const targetX: number = d.target.x - (targetPadding * normX);
647 const targetY: number = d.target.y - (targetPadding * normY);
648 return `M${sourceX},${sourceY}L${targetX},${targetY}`;
649 }).on('dblclick', (d: Tick): void => { this.getDeleteLinkConfirmation(d); });
650 this.vlNode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
651 this.vnfdnode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
652 this.cpnode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
654 /** Update graph (called when needed) at D3 region @private */
655 private restart(node: COMPOSERNODES[]): void {
656 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
657 this.path = this.path.data(this.links);
658 this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES): string => d.id);
659 this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES): string => d.id);
660 this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES): string => d.id);
661 this.resetAndCreateNodes();
662 this.force.nodes(node).force('link').links(this.links);
663 this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
665 /** Rest and create nodes @private */
666 private resetAndCreateNodes(): void {
667 this.path.exit().remove();
668 this.vlNode.exit().remove();
669 this.vnfdnode.exit().remove();
670 this.cpnode.exit().remove();
675 this.path.merge(this.path);
676 this.vlNode = this.gvlNode.merge(this.vlNode);
677 this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
678 this.cpnode = this.gcpNode.merge(this.cpnode);
680 /** setting the Path @private */
681 private getPathNodes(): void {
682 this.path = this.path.enter().append('svg:path');
684 /** Setting all the VL nodes @private */
685 private getVLNodes(): void {
686 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
687 this.gvlNode = this.vlNode.enter().append('svg:g');
688 this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
689 this.gvlNode.append('svg:image')
691 .attr('x', graphContainerAttr.imageX)
692 .attr('y', graphContainerAttr.imageY)
693 .call(this.onDragDrop())
694 .attr('id', (d: COMPOSERNODES): string => d.selectorId)
695 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
696 .attr('xlink:href', 'assets/images/VL.svg')
697 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
698 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
699 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
700 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
701 this.gvlNode.append('svg:text')
702 .attr('class', 'node_text')
703 .attr('y', graphContainerAttr.textY)
704 .text((d: COMPOSERNODES): string => d.id);
706 /** Setting all the VNFD nodes @private */
707 private getVNFDNodes(): void {
708 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
709 this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
710 this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
711 this.gvnfdNode.append('svg:image')
713 .attr('x', graphContainerAttr.imageX)
714 .attr('y', graphContainerAttr.imageY)
715 .call(this.onDragDrop())
716 .attr('id', (d: COMPOSERNODES): string => d.selectorId)
717 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
718 .attr('xlink:href', 'assets/images/VNFD.svg')
719 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
720 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
721 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
722 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
723 this.gvnfdNode.append('svg:text')
724 .attr('class', 'node_text')
725 .attr('y', graphContainerAttr.textY)
726 .text((d: COMPOSERNODES): string => d.name);
728 /** Setting all the CP nodes @private */
729 private getCPNodes(): void {
730 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
731 this.gcpNode = this.cpnode.enter().append('svg:g');
732 this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
733 this.gcpNode.append('svg:image')
735 .attr('x', graphContainerAttr.imageX)
736 .attr('y', graphContainerAttr.imageY)
737 .call(this.onDragDrop())
738 .attr('id', (d: COMPOSERNODES): string => d.selectorId)
739 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
740 .attr('xlink:href', 'assets/images/CP.svg')
741 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
742 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
743 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
744 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
745 this.gcpNode.append('svg:text')
746 .attr('class', 'node_text')
747 .attr('y', graphContainerAttr.textY)
748 .text((d: COMPOSERNODES): string => d.name);
750 /** Events handles when mousemove it will capture the selected node data @private */
751 private mousemove(): void {
752 if (!this.mousedownNode) { return; }
753 this.dragLine.attr('d',
754 `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
756 /** Get confirmation Before Deleting the Link in Topology @private */
757 private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: string): void {
758 let findVNFName: string = '';
759 let findVLDID: string = '';
760 if (mouseData.type === 'vld') {
761 findVNFName = this.mouseupNode.name;
762 findVLDID = this.mousedownNode.id;
763 this.vlName = this.mousedownNode.name;
765 findVNFName = this.mousedownNode.name;
766 findVLDID = this.mouseupNode.id;
767 this.vlName = this.mouseupNode.name;
769 if (getNsData['vnfd-id'] !== undefined) {
770 getNsData['vnfd-id'].forEach((resVNFid: string): void => {
771 if (resVNFid === findVNFName) {
772 this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFD): boolean => vnfList.id === findVNFName)[0];
773 this.setVnfdConnectionPointRef = this.getVNFSelectedData['mgmt-cp'];
774 this.setVnfdName = this.getVNFSelectedData['product-name'];
775 this.selectedVNFProfile = getNsData.df[0]['vnf-profile'];
779 if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
780 // eslint-disable-next-line security/detect-non-literal-fs-filename
781 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
782 modalRef.componentInstance.topologyType = 'Add';
783 modalRef.componentInstance.cpDetails = this.getVNFSelectedData['ext-cpd'];
784 this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
785 vlname: '<b>' + this.vlName + '</b>',
786 vnfdname: '<b>' + this.setVnfdName + '</b>',
787 cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
788 }).subscribe((res: string): void => {
789 modalRef.componentInstance.topologyname = res;
791 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
792 modalRef.result.then((result: MODALCLOSERESPONSEWITHCP): void => {
794 this.generateCPForVNF(this.selectedVNFProfile, result.connection_point, getVLDIndex);
795 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
799 }).catch((): void => {
800 // Catch Navigation Error
804 this.notifierService.notify('error', this.translateService.instant('ERROR'));
807 /** Generate connection point for vnf using vld @private */
808 private generateCPForVNF(result: VNFPROFILE[], cp: string, getVLDIndex: string): void {
809 if (result !== undefined) {
810 result.forEach((resultVNFPROFILE: VNFPROFILE, index: number): void => {
811 if (getVLDIndex === resultVNFPROFILE.id) {
812 resultVNFPROFILE['virtual-link-connectivity'].push({
813 'constituent-cpd-id': [{
814 'constituent-base-element-id': getVLDIndex,
815 'constituent-cpd-id': cp
817 'virtual-link-profile-id': this.vlName
822 Object.assign(result, {
823 'virtual-link-connectivity': [{
824 'constituent-cpd-id': [{
825 'constituent-base-element-id': getVLDIndex,
826 'constituent-cpd-id': cp
828 'virtual-link-profile-id': this.vlName
833 /** Events handles when mousedown click it will capture the selected node data @private */
834 private mouseDown(d: COMPOSERNODES): void {
835 // eslint-disable-next-line deprecation/deprecation
836 event.preventDefault();
837 if (d3.event.ctrlKey) { return; }
838 if (d3.event.shiftKey) {
839 if (d.type === 'vnfd') {
840 this.selectedNode.push(d);
842 this.mousedownNode = d;
843 this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
844 this.currentSelectedLink = null;
845 this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
846 .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
849 /** Event handles when mouseup event occures @private */
850 private mouseUp(d: COMPOSERNODES): void {
851 if (!this.mousedownNode) { return; }
852 this.dragLine.classed('hidden', true).style('marker-end', '');
853 this.mouseupNode = d;
854 if ((this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') ||
855 (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld')) {
856 if (this.mouseupNode.type === 'vnfd') {
857 this.getOldVLDIndex = this.mouseupNode.id.split(':');
858 } else if (this.mousedownNode.type === 'vnfd') {
859 this.getOldVLDIndex = this.mousedownNode.id.split(':');
861 const setOldVLDindex: string = this.getOldVLDIndex[1];
862 this.putType = 'cpAdded';
863 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
864 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
866 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
867 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
869 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
870 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
872 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
873 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
875 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
876 } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
878 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
881 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
883 this.resetMouseVars();
885 this.currentSelectedLink = d;
886 this.currentSelectedNode = null;
888 /** Mosue Drag Line false if it is not satisfied @private */
889 private deselectPath(): void {
890 this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
892 /** reset Mouse varaibles @private */
893 private resetMouseVars(): void {
894 this.mousedownNode = null;
895 this.mouseupNode = null;
896 this.mousedownLink = null;
898 /** De-select all the selected nodes @private */
899 private deselectAllNodes(): void {
900 this.vlNode.select('image').classed(this.activeClass, false);
901 this.vnfdnode.select('image').classed(this.activeClass, false);
902 this.cpnode.select('image').classed(this.activeClass, false);
904 /** Show the right-side information @private */
905 private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
906 this.isShowNSDDetails = nsdDetails;
907 this.isShowVLDetails = vldDetails;
908 this.isShowVNFDetails = vnfDeails;
909 this.isShowCPDetails = cpDetails;
911 /** Events handles when Shift Click to perform create cp @private */
912 // eslint-disable-next-line @typescript-eslint/no-explicit-any
913 private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
914 this.selectNodeExclusive(nodeSelected, d);
916 /** Selected nodes @private */
917 // eslint-disable-next-line @typescript-eslint/no-explicit-any
918 private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
919 const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
920 this.deselectAllNodes();
921 nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
922 if (d.type === 'vld' && !alreadyIsActive) {
923 this.nsData['virtual-link-desc'].forEach((result: VLD): void => {
924 if (result.id === d.id) {
925 this.showRightSideInfo(false, true, false, false);
926 this.virtualLinkDesc = result;
929 } else if (d.type === 'vnfd' && !alreadyIsActive) {
930 this.nsData.df.forEach((res: DF): void => {
931 if (res['vnf-profile'] !== undefined) {
932 res['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
933 if (resVNF.id === d.nodeIndex && resVNF['vnfd-id'] === d.name) {
934 this.showRightSideInfo(false, false, true, false);
935 this.vnfData = resVNF;
940 } else if (d.type === 'ns' && !alreadyIsActive) {
941 this.nsData.df.forEach((resultDF: DF): void => {
942 if (resultDF['vnf-profile'] !== undefined) {
943 resultDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
944 if (resVNF['virtual-link-connectivity'] !== undefined) {
945 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
946 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
947 const connectionPointID: string = resVNF.id + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'];
948 if (connectionPointID === d.id) {
949 this.cpData = resultCCI;
950 this.vnfData = resVNF;
951 this.virtualLinkProfileID = resultVLC['virtual-link-profile-id'];
952 this.showRightSideInfo(false, false, false, true);
961 this.showRightSideInfo(true, false, false, false);
964 /** Get confirmation Before Deleting the Link in Topology @private */
965 private getDeleteLinkConfirmation(d: Tick): void {
966 // eslint-disable-next-line security/detect-non-literal-fs-filename
967 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
968 modalRef.componentInstance.topologyType = 'Delete';
969 modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK') + ' - ' + d.source.id;
970 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
971 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
973 this.doubleClickLink(d);
975 }).catch((): void => {
976 // Catch Navigation Error
979 /** Events handles when Double Click to Delete the link @private */
980 private doubleClickLink(d: Tick): void {
981 let getID: string = '';
982 let getName: string = '';
983 let getNodeIndex: string;
984 if (d.source.type === 'ns') {
986 getName = d.source.name;
987 getNodeIndex = d.source.nodeIndex;
989 /** Split the selected node of connectionpoint */
990 const selectedNode: string[] = getID.split(':');
991 this.nsData.df.forEach((resultDF: DF): void => {
992 if (resultDF['vnf-profile'] !== undefined) {
993 resultDF['vnf-profile'].forEach((elementVNF: VNFPROFILE): void => {
994 const selectedVNFProfileID: string = selectedNode[0];
995 /** If VNF ID is equal to selected VNFProfile ID check the VLC of CCI to match the id and name to remove the VLC index */
996 if (selectedVNFProfileID === elementVNF.id) {
997 elementVNF['virtual-link-connectivity'].forEach((elementVLC: VLC, index: number): void => {
998 const posCCI: number = elementVLC['constituent-cpd-id'].findIndex((e: CCI): boolean => {
999 const getCID: string = elementVNF.id + ':' + e['constituent-base-element-id'] + index + ':' + e['constituent-cpd-id'];
1000 return getID === getCID;
1002 if (posCCI !== -1) {
1003 elementVNF['virtual-link-connectivity'].splice(index, 1);
1010 this.putType = 'linkdelete';
1011 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1013 /** Get confirmation Before Deleting the Node in Topology @private */
1014 private getDeleteConfirmation(d: COMPOSERNODES): void {
1015 // eslint-disable-next-line security/detect-non-literal-fs-filename
1016 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
1017 modalRef.componentInstance.topologyType = 'Delete';
1018 modalRef.componentInstance.topologyname = d.name;
1019 if (d.type === 'vld') {
1020 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
1021 } else if (d.type === 'vnfd') {
1022 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
1023 } else if (d.type === 'ns') {
1024 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
1026 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
1028 this.doubleClick(d);
1030 }).catch((): void => {
1031 // Catch Navigation Error
1034 /** Events handles when Double Click to Delete @private */
1035 private doubleClick(d: COMPOSERNODES): void {
1036 const deletedNode: COMPOSERNODES = d;
1037 this.nodes.forEach((res: COMPOSERNODES, i: number): void => {
1038 if (res.id === d.id) {
1039 if (deletedNode.type === 'vld') {
1040 /** Remove the virtual-link-desc related to VL */
1041 const pos: number = this.nsData['virtual-link-desc'].map((e: VLD): string => e.id).indexOf(d.id);
1042 this.nsData['virtual-link-desc'].splice(pos, 1);
1043 /** Remove the virtual-link-connectivity between VL and VNFD */
1044 this.nsData.df.forEach((resultDF: DF): void => {
1045 if (resultDF['vnf-profile'] !== undefined) {
1046 resultDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
1047 const getVLArray: number[] = resVNF['virtual-link-connectivity'].map((e: VLC, index: number): number => {
1048 if (e['virtual-link-profile-id'] === d.id) {
1052 if (getVLArray.length > 0) {
1053 getVLArray.forEach((removeIndex: number): void => {
1054 const index: string = removeIndex.toString();
1055 // eslint-disable-next-line security/detect-object-injection
1056 resVNF['virtual-link-connectivity'].splice(resVNF['virtual-link-connectivity'][index], 1);
1062 this.putType = 'nsddelete';
1063 } else if (deletedNode.type === 'vnfd') {
1064 this.nsData.df.forEach((resultDF: DF): void => {
1065 if (resultDF['vnf-profile'] !== undefined) {
1066 /** Remove the vnf-profile related to VNFD */
1067 const posVNF: number = resultDF['vnf-profile'].findIndex((e: VNFPROFILE): boolean => e['vnfd-id'] === d.name && e.id === d.nodeIndex);
1068 resultDF['vnf-profile'].splice(posVNF, 1);
1069 /** Check the VNFD exists in any vnf-profile */
1070 const isVNFDExists: boolean = resultDF['vnf-profile'].some((e: VNFPROFILE): boolean => e['vnfd-id'] === d.name);
1071 /** If VNFD not exists in the vnf-profile remove from vnfd-id */
1072 if (!isVNFDExists) {
1073 const posVNFD: number = this.nsData['vnfd-id'].findIndex((e: string): boolean => e === d.name);
1074 this.nsData['vnfd-id'].splice(posVNFD, 1);
1078 this.putType = 'vnfddelete';
1079 } else if (deletedNode.type === 'ns') {
1080 /** Split the selected node */
1081 const selectedNode: string[] = d.id.split(':');
1082 this.nsData.df.forEach((resultDF: DF): void => {
1083 if (resultDF['vnf-profile'] !== undefined) {
1084 resultDF['vnf-profile'].forEach((elementVNF: VNFPROFILE): void => {
1085 const selectedVNFProfileID: string = selectedNode[0];
1086 /** If VNF ID is equal to selected VNFProfile ID check the VLC of CCI to match the id and name to remove the VLC index */
1087 if (selectedVNFProfileID === elementVNF.id) {
1088 elementVNF['virtual-link-connectivity'].forEach((elementVLC: VLC, index: number): void => {
1089 const posCCI: number = elementVLC['constituent-cpd-id'].findIndex((e: CCI): boolean => {
1090 const getID: string = elementVNF.id + ':' + e['constituent-base-element-id'] + index + ':' + e['constituent-cpd-id'];
1091 return d.id === getID;
1093 if (posCCI !== -1) {
1094 elementVNF['virtual-link-connectivity'].splice(index, 1);
1101 this.putType = 'nsdelete';
1103 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1107 /** drag event @private */
1108 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1109 private onDragDrop(): any {
1110 return d3.drag().filter(this.dragFilter)
1111 .on('start', this.dragstarted)
1112 .on('drag', this.dragged)
1113 .on('end', this.dragended);
1115 /** Key press event @private */
1116 private keyDown(): void {
1117 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1118 if (this.lastKeyDown !== -1) { return; }
1119 this.lastKeyDown = d3.event.keyCode;
1120 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1121 this.svg.classed('ctrl', true);
1124 /** Key realse event @private */
1125 private keyUp(): void {
1126 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1127 this.lastKeyDown = -1;
1128 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1129 this.gvlNode.on('.drag', null);
1130 this.gvnfdNode.on('.drag', null);
1131 this.gcpNode.on('.drag', null);
1132 this.svg.classed('ctrl', false);
1135 /** Events handles when to drag using filter for the keys @private */
1136 private dragFilter(): boolean {
1137 return d3.event.ctrlKey && !d3.event.button;
1139 /** Events handles when dragstarted @private */
1140 private dragstarted(d: COMPOSERNODES): void {
1144 /** Events handles when dragged @private */
1145 private dragged(d: COMPOSERNODES): void {
1146 d.fx = d.x = d3.event.x;
1147 d.fy = d.y = d3.event.y;
1149 /** Events handles when dragended @private */
1150 private dragended(d: COMPOSERNODES): void {
1151 if (this.forceSimulationActive) {
1157 this.forceSimulationActive = false;
1160 /** Events handles when node double click @private */
1161 private onNodedblClickToggleSidebar(): void {
1162 this.sideBarOpened = false;
1164 /** Events handles when node single click @private */
1165 private onNodeClickToggleSidebar(): void {
1166 this.sideBarOpened = true;