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 /** Conatins mousedown action @public */
77 public mousedownNode: COMPOSERNODES = null;
78 /** Conatins mouseup action @public */
79 public mouseupNode: COMPOSERNODES = null;
80 /** Conatins mousedownLink action @public */
81 public mousedownLink: COMPOSERNODES = null;
82 /** Conatins current Selection node action @public */
83 public currentSelectedNode: COMPOSERNODES = null;
84 /** Conatins current Selection node action @public */
85 public currentSelectedLink: COMPOSERNODES = null;
86 /** Need to show the NSD Details @public */
87 public isShowNSDDetails: boolean = true;
88 /** Contains the node information of VL @public */
89 public vlNodes: {}[] = [];
90 /** Need to show the VL Details @public */
91 public isShowVLDetails: boolean = false;
92 /** Contains the node information of VNF @public */
93 public vnfNodes: {}[] = [];
94 /** contains the VNF Details @public */
95 public vnfData: VNFPROFILE;
96 /** contains the Virtual Link connectivity Details @public */
97 public virtualLinkProfileID: string;
98 /** Need to show the VNF Details @public */
99 public isShowVNFDetails: boolean = false;
100 /** Contains the node information of CP @public */
101 public cpNodes: {}[] = [];
102 /** Need to show the CP Details */
104 /** Need to show the VNF Details @public */
105 public isShowCPDetails: boolean = false;
106 /** random number count @public */
107 public randomNumberLength: number;
108 /** Contains the vnfd information @public */
109 public vnfList: VNFD[] = [];
110 /** Add the activeclass for the selected @public */
111 public activeClass: string = 'active';
112 /** Add the fixed class for the freeze @public */
113 public fixedClass: string = 'fixed';
114 /** Check the loading results @public */
115 public isLoadingResults: boolean = true;
116 /** Give the message for the loading @public */
117 public message: string = 'PLEASEWAIT';
118 /** Get VNF selected node @public */
119 public getVNFSelectedData: VNFD;
120 /** Assign the forcesimulation active @public */
121 public forceSimulationActive: boolean = false;
122 /** Assign pinned class for the button when freezed @public */
123 public classApplied: boolean = false;
124 /** Contains sidebar open status @public */
125 public sideBarOpened: boolean = false;
126 /** Contains SVG attributes @private */
127 // eslint-disable-next-line @typescript-eslint/no-explicit-any
129 /** Contains the Drag line */
130 // eslint-disable-next-line @typescript-eslint/no-explicit-any
131 private dragLine: any;
132 /** Contains VL node @private */
133 // eslint-disable-next-line @typescript-eslint/no-explicit-any
135 /** Contains VNFD node @private */
136 // eslint-disable-next-line @typescript-eslint/no-explicit-any
137 private vnfdnode: any;
138 /** Contains CP node @private */
139 // eslint-disable-next-line @typescript-eslint/no-explicit-any
141 /** Rendered nodes represent VL @private */
142 // eslint-disable-next-line @typescript-eslint/no-explicit-any
143 private gvlNode: any;
144 /** Rendered nodes represent VL @private */
145 // eslint-disable-next-line @typescript-eslint/no-explicit-any
146 private gvnfdNode: any;
147 /** Rendered nodes represent VL @private */
148 // eslint-disable-next-line @typescript-eslint/no-explicit-any
149 private gcpNode: any;
150 /** Contains forced node animations @private */
151 // eslint-disable-next-line @typescript-eslint/no-explicit-any
153 /** Contains all the selected node @private */
154 private selectedNode: COMPOSERNODES[] = [];
155 /** Contains the connected point @private */
156 private connectionPoint: string;
157 /** Contains id of the node @private */
158 private identifier: string;
159 /** Contains copy of NSD information @private */
160 private nsdCopy: string;
161 /** Contains the VNFD copy @private */
162 private vnfdCopy: string;
163 /** Contains path information of the node */
164 // eslint-disable-next-line @typescript-eslint/no-explicit-any
166 /** Contains the node information @private */
167 private nodes: COMPOSERNODES[] = [];
168 /** Contains the link information of nodes @private */
169 private links: {}[] = [];
170 /** Contains the NS information @private */
171 private nsData: NSDDetails;
172 /** Instance of the rest service @private */
173 private restService: RestService;
174 /** Service holds the router information @private */
175 private router: Router;
176 /** Holds teh instance of AuthService class of type AuthService @private */
177 private activatedRoute: ActivatedRoute;
178 /** Notifier service to popup notification @private */
179 private notifierService: NotifierService;
180 /** Controls the header form @private */
181 private headers: HttpHeaders;
182 /** Contains tranlsate instance @private */
183 private translateService: TranslateService;
184 /** Contains lastkeypressed instance @private */
185 private lastKeyDown: number = -1;
186 /** Instance of the modal service @private */
187 private modalService: NgbModal;
188 /** Setting the Value of connection point refrence of the CP @private */
189 private setVnfdConnectionPointRef: string;
190 /** Setting the Value of VL name for confirmation @private */
191 private vlName: string;
192 /** Setting the Value of VNFD name for confirmation @private */
193 private setVnfdName: string;
194 /** Contains all methods related to shared @private */
195 private sharedService: SharedService;
196 /** Contains selected node VNF profile objects @private */
197 private selectedVNFProfile: VNFPROFILE[];
199 constructor(injector: Injector) {
200 this.injector = injector;
201 this.restService = this.injector.get(RestService);
202 this.dataService = this.injector.get(DataService);
203 this.router = this.injector.get(Router);
204 this.activatedRoute = this.injector.get(ActivatedRoute);
205 this.notifierService = this.injector.get(NotifierService);
206 this.translateService = this.injector.get(TranslateService);
207 this.modalService = this.injector.get(NgbModal);
208 this.sharedService = this.injector.get(SharedService);
210 /** Lifecyle Hooks the trigger before component is instantiated @public */
211 public ngOnInit(): void {
212 this.identifier = this.activatedRoute.snapshot.paramMap.get('id');
214 this.headers = new HttpHeaders({
215 'Content-Type': 'application/gzip',
216 Accept: 'application/json',
217 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0'
220 /** Events handles at drag on D3 region @public */
221 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
222 public drag(ev: any): void {
223 if (ev.target.id === 'vl') {
224 ev.dataTransfer.setData('text', ev.target.id);
226 ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value);
229 /** On clicking redirect to NS edit page @public */
230 public onEdit(): void {
231 this.router.navigate(['/packages/ns/edit/', this.identifier]).catch((): void => {
232 // Catch Navigation Error
235 /** Events handles drop at D3 region @public */
236 public drop(ev: DragEvent): void {
238 const getDropedName: string = ev.dataTransfer.getData('text');
239 if (getDropedName === 'vl') {
240 this.svg.selectAll('*').remove();
241 this.vldropComposer();
243 this.svg.selectAll('*').remove();
244 const vnfdName: string = ev.dataTransfer.getData('text');
245 this.vnfdropComposer(vnfdName);
248 /** Drop VL Composer Data @public */
249 public vldropComposer(): void {
250 this.randomNumberLength = CONSTANTNUMBER.randomNumber;
251 const generateId: string = 'ns_vl_' + this.sharedService.randomString();
252 if (this.nsData['virtual-link-desc'] === undefined) {
253 this.nsData['virtual-link-desc'] = [];
255 this.nsData['virtual-link-desc'].push({
257 'mgmt-network': false
259 this.putType = 'nsdadd';
260 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
262 /** Drop VNFD Composer Data @public */
263 public vnfdropComposer(vnfdName: string): void {
264 const vnfID: string = 'ns_vnfd_' + this.sharedService.randomString();
265 if (this.nsData.df.length > 0) {
266 this.addVNFDID(vnfdName);
267 this.nsData.df.forEach((res: DF): void => {
268 if (res['vnf-profile'] === undefined) {
269 res['vnf-profile'] = [];
271 res['vnf-profile'].push({
273 'virtual-link-connectivity': [],
278 Object.assign(this.nsData.df, {
282 'virtual-link-connectivity': [],
287 this.putType = 'vnfdadd';
288 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
290 /** Add the VNFD-ID while drop VNFD if not exists @public */
291 public addVNFDID(vnfdName: string): void {
292 const vnfdIDArray: string[] = this.nsData['vnfd-id'];
293 if (vnfdIDArray !== undefined) {
294 if (vnfdIDArray.indexOf(vnfdName) === -1) {
295 vnfdIDArray.push(vnfdName);
298 Object.assign(this.nsData, {
299 'vnfd-id': [vnfdName]
303 /** Events handles allow drop on D3 region @public */
304 public allowDrop(ev: DragEvent): void {
307 /** Save NSD Information @public */
308 public saveNSD(): void {
309 if (this.nsPackageDetails.id !== undefined) {
310 this.nsData.id = this.nsPackageDetails.id;
312 if (this.nsPackageDetails.name !== undefined) {
313 this.nsData.name = this.nsPackageDetails.name;
315 if (this.nsPackageDetails.description !== undefined) {
316 this.nsData.description = this.nsPackageDetails.description;
318 if (this.nsPackageDetails.version !== undefined) {
319 this.nsData.version = this.nsPackageDetails.version;
321 if (this.nsPackageDetails.designer !== undefined) {
322 this.nsData.designer = this.nsPackageDetails.designer;
324 this.putType = 'nsdUpdate';
325 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
327 /** Save Virtual Link @public */
328 public saveVL(vlid: string): void {
329 this.nsData['virtual-link-desc'].forEach((result: VLD): void => {
330 if (result.id === vlid) {
331 result['mgmt-network'] = !isNullOrUndefined(this.virtualLinkDesc['mgmt-network']) ? this.virtualLinkDesc['mgmt-network'] : true;
334 this.putType = 'vlUpdate';
335 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
337 /** Add the new Data @public */
338 public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void {
339 this.isLoadingResults = true;
340 let successMessage: string = '';
341 if (putType === 'nsdadd') {
342 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD';
343 } else if (putType === 'vnfdadd') {
344 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD';
345 } else if (putType === 'cpAdded') {
346 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS';
347 } else if (putType === 'nsdUpdate') {
348 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
349 } else if (putType === 'vlUpdate') {
350 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY';
351 } else if (putType === 'nsddelete') {
352 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD';
353 } else if (putType === 'vnfddelete') {
354 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD';
355 } else if (putType === 'nsdelete') {
356 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS';
357 } else if (putType === 'linkdelete') {
358 successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK';
360 /** Below hide for conflicts with light weight UI */
361 const apiURLHeader: APIURLHEADER = {
362 url: apiURL + '/' + identifier + '/nsd_content',
363 httpOptions: { headers: this.headers }
365 const nsData: NSDATACREATION = { nsd: { nsd: [] } };
367 nsData.nsd.nsd.push(data);
368 const descriptorInfo: string = jsyaml.dump(nsData, { sortKeys: true });
369 this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo })
370 .then((content: ArrayBuffer): void => {
371 this.restService.putResource(apiURLHeader, content).subscribe((res: {}): void => {
373 this.notifierService.notify('success', this.translateService.instant(successMessage));
374 this.isLoadingResults = false;
375 }, (error: ERRORDATA): void => {
377 this.restService.handleError(error, 'put');
378 this.isLoadingResults = false;
380 }).catch((): void => {
381 this.notifierService.notify('error', this.translateService.instant('ERROR'));
382 this.isLoadingResults = false;
385 /** Show Info @public */
386 public showInfo(): void {
387 // eslint-disable-next-line security/detect-non-literal-fs-filename
388 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
389 modalRef.componentInstance.topologyType = 'Info';
390 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO');
391 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
395 }).catch((): void => {
396 // Catch Navigation Error
399 /** Event to freeze the animation @public */
400 public onFreeze(): void {
401 this.classApplied = !this.classApplied;
402 const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass);
403 d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive);
404 if (alreadyFixedIsActive) {
407 this.forceSimulationActive = alreadyFixedIsActive;
408 this.nodes.forEach((d: COMPOSERNODES): void => {
409 d.fx = (alreadyFixedIsActive) ? null : d.x;
410 d.fy = (alreadyFixedIsActive) ? null : d.y;
412 if (alreadyFixedIsActive) {
413 this.force.restart();
416 /** Events handles when dragended @public */
417 public toggleSidebar(): void {
418 this.sideBarOpened = !this.sideBarOpened;
419 this.deselectAllNodes();
420 this.showRightSideInfo(true, false, false, false);
422 /** Prepare information for node creation of VNFD @private */
423 private generateData(): void {
424 this.generateVNFData();
425 this.generateDataNSDTopology();
426 this.sideBarOpened = false;
428 /** Prepare the information of the VNFD @private */
429 private generateVNFData(): void {
430 this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFD[]): void => {
431 this.vnfList = vnfdPackageData;
432 }, (error: ERRORDATA): void => {
433 this.restService.handleError(error, 'get');
436 /** Prepare information for node creation of NSD Topology @private */
437 private generateDataNSDTopology(): void {
440 this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails): void => {
441 delete nsData._admin;
443 delete nsData._links;
444 this.nsData = nsData;
445 this.generateNSInfo(nsData);
446 if (nsData['virtual-link-desc'] !== undefined) {
447 /** Details of the VL */
448 this.nsDataVLD(nsData);
450 if (this.nsData.df.length > 0) {
451 this.nsData.df.forEach((res: DF): void => {
452 if (res['vnf-profile'] !== undefined) {
453 /** Details of the VNFD */
454 this.nsDataConstituentVNFD(nsData);
458 if (nsData.df.length > 0) {
459 this.nsDataVLDLinkCreation(nsData);
461 this.separateAndCreatenode();
462 }, (error: ERRORDATA): void => {
463 if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) {
464 this.router.navigateByUrl('404', { skipLocationChange: true }).catch((): void => {
465 // Catch Navigation Error
468 this.restService.handleError(error, 'get');
470 this.isLoadingResults = false;
471 this.isShowNSDDetails = false;
474 /** Generate the NS Package Information */
475 private generateNSInfo(nsData: NSDDetails): void {
476 this.nsPackageDetails.id = nsData.id;
477 this.nsPackageDetails.name = nsData.name;
478 this.nsPackageDetails.description = nsData.description;
479 this.nsPackageDetails.version = nsData.version;
480 this.nsPackageDetails.designer = nsData.designer;
482 /** nsData VL node creation function @private */
483 private nsDataVLD(nsData: NSDDetails): void {
484 nsData['virtual-link-desc'].forEach((res: VLD): void => {
485 this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id });
488 /** nsData VNFD node creation function @private */
489 private nsDataConstituentVNFD(nsData: NSDDetails): void {
490 nsData.df.forEach((resDF: DF): void => {
491 resDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
494 id: resVNF['vnfd-id'] + ':' + resVNF.id,
497 name: resVNF['vnfd-id'],
498 nodeIndex: resVNF.id,
499 selectorId: resVNF['vnfd-id'] + '_' + resVNF.id
501 if (resVNF['virtual-link-connectivity'] !== undefined) {
502 this.nsDataCP(resVNF, resVNF.id);
507 /** nsData CP node creation function @private */
508 private nsDataCP(resVNF: VNFPROFILE, vnfID: string): void {
509 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
510 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
513 id: vnfID + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'],
516 name: resultCCI['constituent-cpd-id'],
517 nodeIndex: resultCCI['constituent-base-element-id'],
518 selectorId: 'osm-' + resultCCI['constituent-cpd-id'] + '-' + vnfID + resultCCI['constituent-base-element-id'] + index
523 /** nsData Link node creation function @private */
524 private nsDataVLDLinkCreation(nsData: NSDDetails): void {
525 nsData.df.forEach((resDF: DF): void => {
526 if (resDF['vnf-profile'] !== undefined) {
527 resDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
528 this.nsdCopy = resVNF['vnfd-id'] + ':' + resVNF.id;
529 if (resVNF['virtual-link-connectivity'] !== undefined) {
530 this.nsDataVNFDConnectionPointRefrence(resVNF);
536 /** nsDataVNFDConnectionPointRefrence undefined Call this function @private */
537 private nsDataVNFDConnectionPointRefrence(resVNF: VNFPROFILE): void {
538 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
539 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
540 this.vnfdCopy = resultVLC['virtual-link-profile-id'];
541 this.connectionPoint = resVNF.id + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'];
542 const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES): string => e.id).indexOf(this.connectionPoint);
543 const nsdPos: number = this.nodes.map((e: COMPOSERNODES): string => e.id).indexOf(this.nsdCopy);
544 const vnfdPos: number = this.nodes.map((e: COMPOSERNODES): string => e.id).indexOf(this.vnfdCopy);
547 // eslint-disable-next-line security/detect-object-injection
548 source: this.nodes[connectionPointPos],
549 // eslint-disable-next-line security/detect-object-injection
550 target: this.nodes[nsdPos]
553 // eslint-disable-next-line security/detect-object-injection
554 source: this.nodes[connectionPointPos],
555 // eslint-disable-next-line security/detect-object-injection
556 target: this.nodes[vnfdPos]
561 /** Separate and create node @private */
562 private separateAndCreatenode(): void {
563 this.seprateNodes(this.nodes);
564 this.createnode(this.nodes);
565 this.isLoadingResults = false;
567 /** Get the default Configuration of containers @private */
568 private getGraphContainerAttr(): GRAPHDETAILS {
581 sourcePaddingYes: 17,
591 /** Separate the nodes along with its tyep @private */
592 private seprateNodes(node: COMPOSERNODES[]): void {
593 this.vlNodes = []; this.vnfNodes = []; this.cpNodes = [];
594 node.forEach((nodeList: COMPOSERNODES): void => {
595 if (nodeList.type === 'vld') {
596 this.vlNodes.push(nodeList);
597 } else if (nodeList.type === 'vnfd') {
598 this.vnfNodes.push(nodeList);
599 } else if (nodeList.type === 'ns') {
600 this.cpNodes.push(nodeList);
604 /** Node is created and render at D3 region @private */
605 private createnode(node: COMPOSERNODES[]): void {
606 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
607 d3.selectAll('svg#graphContainer > *').remove();
608 d3.select(window).on('keydown', (): void => { this.keyDown(); });
609 d3.select(window).on('keyup', (): void => { this.keyUp(); });
610 this.svg = d3.select('#graphContainer')
611 .attr('oncontextmenu', 'return false;')
612 .attr('viewBox', `0 0 ${graphContainerAttr.width} ${graphContainerAttr.height}`)
613 .on('mousemove', (): void => { this.mousemove(); });
614 this.force = d3.forceSimulation()
615 .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength))
616 .force('link', d3.forceLink().id((d: TickPath): string => d.id).distance(graphContainerAttr.distance))
617 .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex,
618 graphContainerAttr.height / graphContainerAttr.forcey))
619 .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex))
620 .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey))
621 .on('tick', (): void => { this.tick(); });
622 this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0');
623 this.path = this.svg.append('svg:g').selectAll('path');
624 this.vlNode = this.svg.append('svg:g').selectAll('vlnode');
625 this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode');
626 this.cpnode = this.svg.append('svg:g').selectAll('cpnode');
630 /** update force layout (called automatically each iteration) @private */
631 private tick(): void {
632 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
633 // draw directed edges with proper padding from node centers
634 this.path.attr('class', 'link').attr('d', (d: Tick): string => {
635 const deltaX: number = d.target.x - d.source.x;
636 const deltaY: number = d.target.y - d.source.y;
637 const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
638 const normX: number = deltaX / dist;
639 const normY: number = deltaY / dist;
640 const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo;
641 const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo;
642 const sourceX: number = d.source.x + (sourcePadding * normX);
643 const sourceY: number = d.source.y + (sourcePadding * normY);
644 const targetX: number = d.target.x - (targetPadding * normX);
645 const targetY: number = d.target.y - (targetPadding * normY);
646 return `M${sourceX},${sourceY}L${targetX},${targetY}`;
647 }).on('dblclick', (d: Tick): void => { this.getDeleteLinkConfirmation(d); });
648 this.vlNode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
649 this.vnfdnode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
650 this.cpnode.attr('transform', (t: TickPath): string => `translate(${t.x},${t.y})`);
652 /** Update graph (called when needed) at D3 region @private */
653 private restart(node: COMPOSERNODES[]): void {
654 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
655 this.path = this.path.data(this.links);
656 this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES): string => d.id);
657 this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES): string => d.id);
658 this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES): string => d.id);
659 this.resetAndCreateNodes();
660 this.force.nodes(node).force('link').links(this.links);
661 this.force.alphaTarget(graphContainerAttr.alphaTarget).restart();
663 /** Rest and create nodes @private */
664 private resetAndCreateNodes(): void {
665 this.path.exit().remove();
666 this.vlNode.exit().remove();
667 this.vnfdnode.exit().remove();
668 this.cpnode.exit().remove();
673 this.path.merge(this.path);
674 this.vlNode = this.gvlNode.merge(this.vlNode);
675 this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode);
676 this.cpnode = this.gcpNode.merge(this.cpnode);
678 /** setting the Path @private */
679 private getPathNodes(): void {
680 this.path = this.path.enter().append('svg:path');
682 /** Setting all the VL nodes @private */
683 private getVLNodes(): void {
684 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
685 this.gvlNode = this.vlNode.enter().append('svg:g');
686 this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
687 this.gvlNode.append('svg:image')
689 .attr('x', graphContainerAttr.imageX)
690 .attr('y', graphContainerAttr.imageY)
691 .call(this.onDragDrop())
692 .attr('id', (d: COMPOSERNODES): string => d.selectorId)
693 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
694 .attr('xlink:href', 'assets/images/VL.svg')
695 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
696 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
697 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); })
698 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
699 this.gvlNode.append('svg:text')
700 .attr('class', 'node_text')
701 .attr('y', graphContainerAttr.textY)
702 .text((d: COMPOSERNODES): string => d.id);
704 /** Setting all the VNFD nodes @private */
705 private getVNFDNodes(): void {
706 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
707 this.gvnfdNode = this.vnfdnode.enter().append('svg:g');
708 this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
709 this.gvnfdNode.append('svg:image')
711 .attr('x', graphContainerAttr.imageX)
712 .attr('y', graphContainerAttr.imageY)
713 .call(this.onDragDrop())
714 .attr('id', (d: COMPOSERNODES): string => d.selectorId)
715 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
716 .attr('xlink:href', 'assets/images/VNFD.svg')
717 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
718 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
719 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); })
720 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
721 this.gvnfdNode.append('svg:text')
722 .attr('class', 'node_text')
723 .attr('y', graphContainerAttr.textY)
724 .text((d: COMPOSERNODES): string => d.name);
726 /** Setting all the CP nodes @private */
727 private getCPNodes(): void {
728 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
729 this.gcpNode = this.cpnode.enter().append('svg:g');
730 this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee');
731 this.gcpNode.append('svg:image')
733 .attr('x', graphContainerAttr.imageX)
734 .attr('y', graphContainerAttr.imageY)
735 .call(this.onDragDrop())
736 .attr('id', (d: COMPOSERNODES): string => d.selectorId)
737 .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight)
738 .attr('xlink:href', 'assets/images/CP.svg')
739 .on('mousedown', (d: COMPOSERNODES): void => { this.mouseDown(d); })
740 .on('mouseup', (d: COMPOSERNODES): void => { this.mouseUp(d); })
741 .on('click', (d: COMPOSERNODES): void => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); })
742 .on('dblclick', (d: COMPOSERNODES): void => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); });
743 this.gcpNode.append('svg:text')
744 .attr('class', 'node_text')
745 .attr('y', graphContainerAttr.textY)
746 .text((d: COMPOSERNODES): string => d.name);
748 /** Events handles when mousemove it will capture the selected node data @private */
749 private mousemove(): void {
750 if (!this.mousedownNode) { return; }
751 this.dragLine.attr('d',
752 `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`);
754 /** Get confirmation Before Deleting the Link in Topology @private */
755 private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: string): void {
756 let findVNFName: string = '';
757 let findVLDID: string = '';
758 if (mouseData.type === 'vld') {
759 findVNFName = this.mouseupNode.name;
760 findVLDID = this.mousedownNode.id;
761 this.vlName = this.mousedownNode.name;
763 findVNFName = this.mousedownNode.name;
764 findVLDID = this.mouseupNode.id;
765 this.vlName = this.mouseupNode.name;
767 if (getNsData['vnfd-id'] !== undefined) {
768 getNsData['vnfd-id'].forEach((resVNFid: string): void => {
769 if (resVNFid === findVNFName) {
770 this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFD): boolean => vnfList.id === findVNFName)[0];
771 this.setVnfdConnectionPointRef = this.getVNFSelectedData['mgmt-cp'];
772 this.setVnfdName = this.getVNFSelectedData['product-name'];
773 this.selectedVNFProfile = getNsData.df[0]['vnf-profile'];
777 if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) {
778 // eslint-disable-next-line security/detect-non-literal-fs-filename
779 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
780 modalRef.componentInstance.topologyType = 'Add';
781 modalRef.componentInstance.cpDetails = this.getVNFSelectedData['ext-cpd'];
782 this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', {
783 vlname: '<b>' + this.vlName + '</b>',
784 vnfdname: '<b>' + this.setVnfdName + '</b>',
785 cpname: '<b>' + this.setVnfdConnectionPointRef + '</b>'
786 }).subscribe((res: string): void => {
787 modalRef.componentInstance.topologyname = res;
789 modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT');
790 modalRef.result.then((result: MODALCLOSERESPONSEWITHCP): void => {
792 this.generateCPForVNF(this.selectedVNFProfile, result.connection_point, getVLDIndex);
793 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType);
797 }).catch((): void => {
798 // Catch Navigation Error
802 this.notifierService.notify('error', this.translateService.instant('ERROR'));
805 /** Generate connection point for vnf using vld @private */
806 private generateCPForVNF(result: VNFPROFILE[], cp: string, getVLDIndex: string): void {
807 if (result !== undefined) {
808 result.forEach((resultVNFPROFILE: VNFPROFILE, index: number): void => {
809 if (getVLDIndex === resultVNFPROFILE.id) {
810 resultVNFPROFILE['virtual-link-connectivity'].push({
811 'constituent-cpd-id': [{
812 'constituent-base-element-id': getVLDIndex,
813 'constituent-cpd-id': cp
815 'virtual-link-profile-id': this.vlName
820 Object.assign(result, {
821 'virtual-link-connectivity': [{
822 'constituent-cpd-id': [{
823 'constituent-base-element-id': getVLDIndex,
824 'constituent-cpd-id': cp
826 'virtual-link-profile-id': this.vlName
831 /** Events handles when mousedown click it will capture the selected node data @private */
832 private mouseDown(d: COMPOSERNODES): void {
833 // eslint-disable-next-line deprecation/deprecation
834 event.preventDefault();
835 if (d3.event.ctrlKey) { return; }
836 if (d3.event.shiftKey) {
837 if (d.type === 'vnfd') {
838 this.selectedNode.push(d);
840 this.mousedownNode = d;
841 this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode;
842 this.currentSelectedLink = null;
843 this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false)
844 .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`);
847 /** Event handles when mouseup event occures @private */
848 private mouseUp(d: COMPOSERNODES): void {
849 if (!this.mousedownNode) { return; }
850 this.dragLine.classed('hidden', true).style('marker-end', '');
851 this.mouseupNode = d;
852 if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') {
853 const getOldVLDIndex: string[] = this.mouseupNode.id.split(':');
854 const setOldVLDindex: string = getOldVLDIndex[1];
855 this.putType = 'cpAdded';
856 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
857 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') {
858 const getOldVLDIndex: string[] = this.mousedownNode.id.split(':');
859 const setOldVLDindex: string = getOldVLDIndex[1];
860 this.putType = 'cpAdded';
861 this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex);
862 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') {
864 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
865 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') {
867 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP'));
868 } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') {
870 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL'));
871 } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') {
873 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF'));
874 } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') {
876 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP'));
879 this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF'));
881 this.resetMouseVars();
883 this.currentSelectedLink = d;
884 this.currentSelectedNode = null;
886 /** Mosue Drag Line false if it is not satisfied @private */
887 private deselectPath(): void {
888 this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0');
890 /** reset Mouse varaibles @private */
891 private resetMouseVars(): void {
892 this.mousedownNode = null;
893 this.mouseupNode = null;
894 this.mousedownLink = null;
896 /** De-select all the selected nodes @private */
897 private deselectAllNodes(): void {
898 this.vlNode.select('image').classed(this.activeClass, false);
899 this.vnfdnode.select('image').classed(this.activeClass, false);
900 this.cpnode.select('image').classed(this.activeClass, false);
902 /** Show the right-side information @private */
903 private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void {
904 this.isShowNSDDetails = nsdDetails;
905 this.isShowVLDetails = vldDetails;
906 this.isShowVNFDetails = vnfDeails;
907 this.isShowCPDetails = cpDetails;
909 /** Events handles when Shift Click to perform create cp @private */
910 // eslint-disable-next-line @typescript-eslint/no-explicit-any
911 private singleClick(nodeSelected: any, d: COMPOSERNODES): void {
912 this.selectNodeExclusive(nodeSelected, d);
914 /** Selected nodes @private */
915 // eslint-disable-next-line @typescript-eslint/no-explicit-any
916 private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void {
917 const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass);
918 this.deselectAllNodes();
919 nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive);
920 if (d.type === 'vld' && !alreadyIsActive) {
921 this.nsData['virtual-link-desc'].forEach((result: VLD): void => {
922 if (result.id === d.id) {
923 this.showRightSideInfo(false, true, false, false);
924 this.virtualLinkDesc = result;
927 } else if (d.type === 'vnfd' && !alreadyIsActive) {
928 this.nsData.df.forEach((res: DF): void => {
929 if (res['vnf-profile'] !== undefined) {
930 res['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
931 if (resVNF.id === d.nodeIndex && resVNF['vnfd-id'] === d.name) {
932 this.showRightSideInfo(false, false, true, false);
933 this.vnfData = resVNF;
938 } else if (d.type === 'ns' && !alreadyIsActive) {
939 this.nsData.df.forEach((resultDF: DF): void => {
940 if (resultDF['vnf-profile'] !== undefined) {
941 resultDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
942 if (resVNF['virtual-link-connectivity'] !== undefined) {
943 resVNF['virtual-link-connectivity'].forEach((resultVLC: VLC, index: number): void => {
944 resultVLC['constituent-cpd-id'].forEach((resultCCI: CCI): void => {
945 const connectionPointID: string = resVNF.id + ':' + resultCCI['constituent-base-element-id'] + index + ':' + resultCCI['constituent-cpd-id'];
946 if (connectionPointID === d.id) {
947 this.cpData = resultCCI;
948 this.vnfData = resVNF;
949 this.virtualLinkProfileID = resultVLC['virtual-link-profile-id'];
950 this.showRightSideInfo(false, false, false, true);
959 this.showRightSideInfo(true, false, false, false);
962 /** Get confirmation Before Deleting the Link in Topology @private */
963 private getDeleteLinkConfirmation(d: Tick): void {
964 // eslint-disable-next-line security/detect-non-literal-fs-filename
965 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
966 modalRef.componentInstance.topologyType = 'Delete';
967 modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK') + ' - ' + d.source.id;
968 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK';
969 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
971 this.doubleClickLink(d);
973 }).catch((): void => {
974 // Catch Navigation Error
977 /** Events handles when Double Click to Delete the link @private */
978 private doubleClickLink(d: Tick): void {
979 let getID: string = '';
980 let getName: string = '';
981 let getNodeIndex: string;
982 if (d.source.type === 'ns') {
984 getName = d.source.name;
985 getNodeIndex = d.source.nodeIndex;
987 /** Split the selected node of connectionpoint */
988 const selectedNode: string[] = getID.split(':');
989 this.nsData.df.forEach((resultDF: DF): void => {
990 if (resultDF['vnf-profile'] !== undefined) {
991 resultDF['vnf-profile'].forEach((elementVNF: VNFPROFILE): void => {
992 const selectedVNFProfileID: string = selectedNode[0];
993 /** 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 */
994 if (selectedVNFProfileID === elementVNF.id) {
995 elementVNF['virtual-link-connectivity'].forEach((elementVLC: VLC, index: number): void => {
996 const posCCI: number = elementVLC['constituent-cpd-id'].findIndex((e: CCI): boolean => {
997 const getCID: string = elementVNF.id + ':' + e['constituent-base-element-id'] + index + ':' + e['constituent-cpd-id'];
998 return getID === getCID;
1000 if (posCCI !== -1) {
1001 elementVNF['virtual-link-connectivity'].splice(index, 1);
1008 this.putType = 'linkdelete';
1009 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1011 /** Get confirmation Before Deleting the Node in Topology @private */
1012 private getDeleteConfirmation(d: COMPOSERNODES): void {
1013 // eslint-disable-next-line security/detect-non-literal-fs-filename
1014 const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' });
1015 modalRef.componentInstance.topologyType = 'Delete';
1016 modalRef.componentInstance.topologyname = d.name;
1017 if (d.type === 'vld') {
1018 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK';
1019 } else if (d.type === 'vnfd') {
1020 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF';
1021 } else if (d.type === 'ns') {
1022 modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT';
1024 modalRef.result.then((result: MODALCLOSERESPONSEDATA): void => {
1026 this.doubleClick(d);
1028 }).catch((): void => {
1029 // Catch Navigation Error
1032 /** Events handles when Double Click to Delete @private */
1033 private doubleClick(d: COMPOSERNODES): void {
1034 const deletedNode: COMPOSERNODES = d;
1035 this.nodes.forEach((res: COMPOSERNODES, i: number): void => {
1036 if (res.id === d.id) {
1037 if (deletedNode.type === 'vld') {
1038 /** Remove the virtual-link-desc related to VL */
1039 const pos: number = this.nsData['virtual-link-desc'].map((e: VLD): string => e.id).indexOf(d.id);
1040 this.nsData['virtual-link-desc'].splice(pos, 1);
1041 /** Remove the virtual-link-connectivity between VL and VNFD */
1042 this.nsData.df.forEach((resultDF: DF): void => {
1043 if (resultDF['vnf-profile'] !== undefined) {
1044 resultDF['vnf-profile'].forEach((resVNF: VNFPROFILE): void => {
1045 const getVLArray: number[] = resVNF['virtual-link-connectivity'].map((e: VLC, index: number): number => {
1046 if (e['virtual-link-profile-id'] === d.id) {
1050 if (getVLArray.length > 0) {
1051 getVLArray.forEach((removeIndex: number): void => {
1052 const index: string = removeIndex.toString();
1053 // eslint-disable-next-line security/detect-object-injection
1054 resVNF['virtual-link-connectivity'].splice(resVNF['virtual-link-connectivity'][index], 1);
1060 this.putType = 'nsddelete';
1061 } else if (deletedNode.type === 'vnfd') {
1062 this.nsData.df.forEach((resultDF: DF): void => {
1063 if (resultDF['vnf-profile'] !== undefined) {
1064 /** Remove the vnf-profile related to VNFD */
1065 const posVNF: number = resultDF['vnf-profile'].findIndex((e: VNFPROFILE): boolean => e['vnfd-id'] === d.name && e.id === d.nodeIndex);
1066 resultDF['vnf-profile'].splice(posVNF, 1);
1067 /** Check the VNFD exists in any vnf-profile */
1068 const isVNFDExists: boolean = resultDF['vnf-profile'].some((e: VNFPROFILE): boolean => e['vnfd-id'] === d.name);
1069 /** If VNFD not exists in the vnf-profile remove from vnfd-id */
1070 if (!isVNFDExists) {
1071 const posVNFD: number = this.nsData['vnfd-id'].findIndex((e: string): boolean => e === d.name);
1072 this.nsData['vnfd-id'].splice(posVNFD, 1);
1076 this.putType = 'vnfddelete';
1077 } else if (deletedNode.type === 'ns') {
1078 /** Split the selected node */
1079 const selectedNode: string[] = d.id.split(':');
1080 this.nsData.df.forEach((resultDF: DF): void => {
1081 if (resultDF['vnf-profile'] !== undefined) {
1082 resultDF['vnf-profile'].forEach((elementVNF: VNFPROFILE): void => {
1083 const selectedVNFProfileID: string = selectedNode[0];
1084 /** 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 */
1085 if (selectedVNFProfileID === elementVNF.id) {
1086 elementVNF['virtual-link-connectivity'].forEach((elementVLC: VLC, index: number): void => {
1087 const posCCI: number = elementVLC['constituent-cpd-id'].findIndex((e: CCI): boolean => {
1088 const getID: string = elementVNF.id + ':' + e['constituent-base-element-id'] + index + ':' + e['constituent-cpd-id'];
1089 return d.id === getID;
1091 if (posCCI !== -1) {
1092 elementVNF['virtual-link-connectivity'].splice(index, 1);
1099 this.putType = 'nsdelete';
1101 this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType);
1105 /** drag event @private */
1106 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1107 private onDragDrop(): any {
1108 return d3.drag().filter(this.dragFilter)
1109 .on('start', this.dragstarted)
1110 .on('drag', this.dragged)
1111 .on('end', this.dragended);
1113 /** Key press event @private */
1114 private keyDown(): void {
1115 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1116 if (this.lastKeyDown !== -1) { return; }
1117 this.lastKeyDown = d3.event.keyCode;
1118 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1119 this.svg.classed('ctrl', true);
1122 /** Key realse event @private */
1123 private keyUp(): void {
1124 const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr();
1125 this.lastKeyDown = -1;
1126 if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) {
1127 this.gvlNode.on('.drag', null);
1128 this.gvnfdNode.on('.drag', null);
1129 this.gcpNode.on('.drag', null);
1130 this.svg.classed('ctrl', false);
1133 /** Events handles when to drag using filter for the keys @private */
1134 private dragFilter(): boolean {
1135 return d3.event.ctrlKey && !d3.event.button;
1137 /** Events handles when dragstarted @private */
1138 private dragstarted(d: COMPOSERNODES): void {
1142 /** Events handles when dragged @private */
1143 private dragged(d: COMPOSERNODES): void {
1144 d.fx = d.x = d3.event.x;
1145 d.fy = d.y = d3.event.y;
1147 /** Events handles when dragended @private */
1148 private dragended(d: COMPOSERNODES): void {
1149 if (this.forceSimulationActive) {
1155 this.forceSimulationActive = false;
1158 /** Events handles when node double click @private */
1159 private onNodedblClickToggleSidebar(): void {
1160 this.sideBarOpened = false;
1162 /** Events handles when node single click @private */
1163 private onNodeClickToggleSidebar(): void {
1164 this.sideBarOpened = true;