From 4487ab5167e1958dca64d5eb008f37960e78fd9f Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Tue, 6 Aug 2019 00:15:28 -0700 Subject: [PATCH 01/11] Update template-list-dialog --- .../template-list-dialog.component.html | 2 +- .../template-list-dialog.component.scss | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/app/components/template/template-list-dialog/template-list-dialog.component.html b/src/app/components/template/template-list-dialog/template-list-dialog.component.html index 346b52d8..3eea74e5 100644 --- a/src/app/components/template/template-list-dialog/template-list-dialog.component.html +++ b/src/app/components/template/template-list-dialog/template-list-dialog.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/app/components/template/template-list-dialog/template-list-dialog.component.scss b/src/app/components/template/template-list-dialog/template-list-dialog.component.scss index 6c455eef..6b2a2818 100644 --- a/src/app/components/template/template-list-dialog/template-list-dialog.component.scss +++ b/src/app/components/template/template-list-dialog/template-list-dialog.component.scss @@ -16,3 +16,26 @@ font-size: 16px; flex-grow: 1; } + +div { + scrollbar-color: darkgrey #263238; + scrollbar-width: thin; +} + +mat-table { + scrollbar-color: darkgrey #263238; + scrollbar-width: thin; +} + +::-webkit-scrollbar { + width: 0.5em; +} + +::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); +} + +::-webkit-scrollbar-thumb { +background-color: darkgrey; +outline: 1px solid #263238; +} From 162806b2681f13439ef97111c15da57aebf7a13c Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Thu, 8 Aug 2019 05:44:35 -0700 Subject: [PATCH 02/11] Information dialog added --- src/app/app.module.ts | 13 +++- .../show-node-action.component.html | 4 ++ .../show-node-action.component.ts | 26 +++++++ .../context-menu/context-menu.component.html | 4 ++ .../info-dialog/info-dialog.component.html | 27 ++++++++ .../info-dialog/info-dialog.component.scss | 3 + .../info-dialog/info-dialog.component.ts | 31 +++++++++ src/app/services/info.service.ts | 67 +++++++++++++++++++ 8 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.html create mode 100644 src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.ts create mode 100644 src/app/components/project-map/info-dialog/info-dialog.component.html create mode 100644 src/app/components/project-map/info-dialog/info-dialog.component.scss create mode 100644 src/app/components/project-map/info-dialog/info-dialog.component.ts create mode 100644 src/app/services/info.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4242bce2..75940652 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -191,6 +191,9 @@ import { DuplicateActionComponent } from './components/project-map/context-menu/ import { MapSettingService } from './services/mapsettings.service'; import { ProjectMapMenuComponent } from './components/project-map/project-map-menu/project-map-menu.component'; import { HelpComponent } from './components/help/help.component'; +import { ShowNodeActionComponent } from './components/project-map/context-menu/actions/show-node-action/show-node-action.component'; +import { InfoDialogComponent } from './components/project-map/info-dialog/info-dialog.component'; +import { InfoService } from './services/info.service'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -309,10 +312,12 @@ if (environment.production) { NodesMenuComponent, AdbutlerComponent, ConsoleDeviceActionComponent, + ShowNodeActionComponent, ConsoleComponent, NodesMenuComponent, ProjectMapMenuComponent, - HelpComponent + HelpComponent, + InfoDialogComponent ], imports: [ BrowserModule, @@ -388,7 +393,8 @@ if (environment.production) { NodeCreatedLabelStylesFixer, NonNegativeValidator, RotationValidator, - MapSettingService + MapSettingService, + InfoService ], entryComponents: [ AddServerDialogComponent, @@ -404,7 +410,8 @@ if (environment.production) { SymbolsComponent, DeleteConfirmationDialogComponent, HelpDialogComponent, - StartCaptureDialogComponent + StartCaptureDialogComponent, + InfoDialogComponent ], bootstrap: [AppComponent] }) diff --git a/src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.html b/src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.html new file mode 100644 index 00000000..b92a0071 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.ts b/src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.ts new file mode 100644 index 00000000..3f70d051 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/show-node-action/show-node-action.component.ts @@ -0,0 +1,26 @@ +import { Component, Input, OnInit, OnChanges } from '@angular/core'; +import { Node } from '../../../../../cartography/models/node'; +import { MatDialog } from '@angular/material'; +import { InfoDialogComponent } from '../../../info-dialog/info-dialog.component'; +import { Server } from '../../../../../models/server'; + +@Component({ + selector: 'app-show-node-action', + templateUrl: './show-node-action.component.html' +}) +export class ShowNodeActionComponent { + @Input() node: Node; + @Input() server: Server + + constructor(private dialog: MatDialog) {} + + showNode() { + const dialogRef = this.dialog.open(InfoDialogComponent, { + width: '600px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.node = this.node; + instance.server = this.server; + } +} diff --git a/src/app/components/project-map/context-menu/context-menu.component.html b/src/app/components/project-map/context-menu/context-menu.component.html index 2ebfeb6a..9e857519 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.html +++ b/src/app/components/project-map/context-menu/context-menu.component.html @@ -1,6 +1,10 @@
+ {{node.name}} + + + +
+ +
diff --git a/src/app/components/project-map/info-dialog/info-dialog.component.scss b/src/app/components/project-map/info-dialog/info-dialog.component.scss new file mode 100644 index 00000000..e28aaee8 --- /dev/null +++ b/src/app/components/project-map/info-dialog/info-dialog.component.scss @@ -0,0 +1,3 @@ +.textBox { + margin-top: 10px; +} diff --git a/src/app/components/project-map/info-dialog/info-dialog.component.ts b/src/app/components/project-map/info-dialog/info-dialog.component.ts new file mode 100644 index 00000000..11968f47 --- /dev/null +++ b/src/app/components/project-map/info-dialog/info-dialog.component.ts @@ -0,0 +1,31 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; +import { Node } from '../../../cartography/models/node'; +import { InfoService } from '../../../services/info.service'; +import { Server } from '../../../models/server'; + +@Component({ + selector: 'app-info-dialog', + templateUrl: './info-dialog.component.html', + styleUrls: ['./info-dialog.component.scss'] +}) +export class InfoDialogComponent implements OnInit { + @Input() server: Server; + @Input() node: Node; + infoList: string[] = []; + commandLine: string = ''; + + constructor( + public dialogRef: MatDialogRef, + private infoService: InfoService + ) {} + + ngOnInit() { + this.infoList = this.infoService.getInfoAboutNode(this.node, this.server); + this.commandLine = this.infoService.getCommandLine(this.node); + } + + onCloseClick() { + this.dialogRef.close(); + } +} diff --git a/src/app/services/info.service.ts b/src/app/services/info.service.ts new file mode 100644 index 00000000..62f58f7a --- /dev/null +++ b/src/app/services/info.service.ts @@ -0,0 +1,67 @@ +import { Injectable } from "@angular/core"; +import { Node } from '../cartography/models/node'; +import { Port } from '../models/port'; +import { Server } from '../models/server'; + +@Injectable() +export class InfoService { + getInfoAboutNode(node: Node, server: Server): string[] { + let infoList: string[] = []; + if (node.node_type === 'cloud') { + infoList.push(`Cloud ${node.name} is always on.`); + } else if (node.node_type === 'nat') { + infoList.push(`NAT ${node.name} is always on.`); + } else if (node.node_type === 'ethernet-hub') { + infoList.push(`Ethernet hub ${node.name} is always on.`); + } else if (node.node_type === 'ethernet_switch') { + infoList.push(`Ethernet switch ${node.name} is always on.`); + } else if (node.node_type === 'frame_relay_switch') { + infoList.push(`Frame relay switch ${node.name} is always on.`); + } else if (node.node_type === 'atm_switch') { + infoList.push(`ATM switch ${node.name} is always on.`); + } else if (node.node_type === 'docker') { + infoList.push(`Docker ${node.name} is ${node.status}.`); + } else if (node.node_type === 'dynamips') { + infoList.push(`Dynamips ${node.name} is always on.`); + } else if (node.node_type === 'traceng') { + infoList.push(`TraceNG ${node.name} is always on.`); + } else if (node.node_type === 'virtualbox') { + infoList.push(`VirtualBox VM ${node.name} is ${node.status}.`); + } else if (node.node_type === 'vmware') { + infoList.push(`VMware VM ${node.name} is ${node.status}.`); + } else if (node.node_type === 'qemu') { + infoList.push(`QEMU VM ${node.name} is ${node.status}.`); + } else if (node.node_type === 'iou') { + infoList.push(`IOU ${node.name} is always on.`); + } else if (node.node_type === 'vpcs') { + infoList.push(`Node ${node.name} is ${node.status}.`); + } + infoList.push(`Running on server ${server.name} with port ${server.port}.`); + infoList.push(`Server ID is ${server.id}.`); + if (node.console_type !== 'none' && node.console_type !== 'null') { + infoList.push(`Console is on port ${node.console} and type is ${node.console_type}.`); + } + infoList = infoList.concat(this.getInfoAboutPorts(node.ports)); + return infoList; + } + + getInfoAboutPorts(ports: Port[]): string { + let response: string = `ports: ` + ports.forEach(port => { + response = response + `adapter_number: ${port.adapter_number}, + link_type: ${port.link_type}, + name: ${port.name}, + port_number: ${port.port_number}, + short_name: ${port.short_name}, ` + }); + return response; + } + + getCommandLine(node: Node): string { + if (node.node_type === 'cloud' || "nat" || "ethernet_hub" || "ethernet_switch" || "frame_relay_switch" || "atm_switch" || "dynamips" || "traceng" || "iou") { + return 'Command line information is not supported for this type of node.'; + } else { + return node.command_line; + } + } +} From c70d9852f668c32110d3d771c10dafcd947beee2 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Thu, 8 Aug 2019 06:25:28 -0700 Subject: [PATCH 03/11] Displaying ports updated --- .../info-dialog/info-dialog.component.html | 12 +++++------- src/app/services/info.service.ts | 16 +++++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/app/components/project-map/info-dialog/info-dialog.component.html b/src/app/components/project-map/info-dialog/info-dialog.component.html index 4bf096d9..96a2234a 100644 --- a/src/app/components/project-map/info-dialog/info-dialog.component.html +++ b/src/app/components/project-map/info-dialog/info-dialog.component.html @@ -4,19 +4,17 @@
- - - {{info}} - - +
+ {{info}} +
Please start the node in order to get the command line information.
-
- {{node.command_line}} +
+ {{command_line}}
diff --git a/src/app/services/info.service.ts b/src/app/services/info.service.ts index 62f58f7a..0e722927 100644 --- a/src/app/services/info.service.ts +++ b/src/app/services/info.service.ts @@ -46,14 +46,12 @@ export class InfoService { } getInfoAboutPorts(ports: Port[]): string { - let response: string = `ports: ` + let response: string = `Ports: ` ports.forEach(port => { - response = response + `adapter_number: ${port.adapter_number}, - link_type: ${port.link_type}, - name: ${port.name}, - port_number: ${port.port_number}, - short_name: ${port.short_name}, ` + response += `link_type: ${port.link_type}, + name: ${port.name}; ` }); + response = response.substring(0, response.length - 2); return response; } @@ -61,7 +59,11 @@ export class InfoService { if (node.node_type === 'cloud' || "nat" || "ethernet_hub" || "ethernet_switch" || "frame_relay_switch" || "atm_switch" || "dynamips" || "traceng" || "iou") { return 'Command line information is not supported for this type of node.'; } else { - return node.command_line; + if (node.status === 'started') { + return node.command_line; + } else { + return 'Please start the node in order to get the command line information.'; + } } } } From c0ef682e9c82d2cafa358f54513cddc27ce8fd6d Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Fri, 9 Aug 2019 06:32:58 -0700 Subject: [PATCH 04/11] Duplicate action added --- src/app/components/projects/projects.component.html | 3 +++ src/app/components/projects/projects.component.ts | 6 ++++++ src/app/services/project.service.spec.ts | 7 +++++++ src/app/services/project.service.ts | 4 ++++ 4 files changed, 20 insertions(+) diff --git a/src/app/components/projects/projects.component.html b/src/app/components/projects/projects.component.html index 7f785447..107c070b 100644 --- a/src/app/components/projects/projects.component.html +++ b/src/app/components/projects/projects.component.html @@ -40,6 +40,9 @@ + diff --git a/src/app/components/projects/projects.component.ts b/src/app/components/projects/projects.component.ts index ab50f90f..1c7fa38a 100644 --- a/src/app/components/projects/projects.component.ts +++ b/src/app/components/projects/projects.component.ts @@ -107,6 +107,12 @@ export class ProjectsComponent implements OnInit { ); } + duplicate(project: Project) { + this.projectService.duplicate(this.server, project.project_id, project.name).subscribe(() => { + this.refresh(); + }); + } + addBlankProject() { const dialogRef = this.dialog.open(AddBlankProjectDialogComponent, { width: '400px', diff --git a/src/app/services/project.service.spec.ts b/src/app/services/project.service.spec.ts index 2752923a..0bad72bc 100644 --- a/src/app/services/project.service.spec.ts +++ b/src/app/services/project.service.spec.ts @@ -132,6 +132,13 @@ describe('ProjectService', () => { expect(req.request.method).toEqual('DELETE'); }); + it('should duplicate the project', () => { + service.duplicate(server, 'projectId', 'projectName').subscribe(); + + const req = httpTestingController.expectOne('http://127.0.0.1:3080/v2/projects/projectId/duplicate'); + expect(req.request.method).toEqual('POST'); + }); + it('should get notifications path of project', () => { const path = service.notificationsPath(server, 'myproject'); expect(path).toEqual('ws://127.0.0.1:3080/v2/projects/myproject/notifications/ws'); diff --git a/src/app/services/project.service.ts b/src/app/services/project.service.ts index 4ca87f98..181e26c5 100644 --- a/src/app/services/project.service.ts +++ b/src/app/services/project.service.ts @@ -49,6 +49,10 @@ export class ProjectService { return this.httpServer.delete(server, `/projects/${project_id}`); } + duplicate(server: Server, project_id: string, project_name): Observable { + return this.httpServer.post(server, `/projects/${project_id}/duplicate`, { name: project_name }); + } + notificationsPath(server: Server, project_id: string): string { return `ws://${server.host}:${server.port}/v2/projects/${project_id}/notifications/ws`; } From 85817535207c30c81ffaac5384c696008cb1a780 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Mon, 12 Aug 2019 08:46:15 -0700 Subject: [PATCH 05/11] Initial implementation of topology summary --- .../topology-summary.component.html | 0 .../topology-summary.component.scss | 0 .../topology-summary.component.spec.ts | 0 .../topology-summary.component.ts | 47 +++++++++++++++++++ src/app/models/project-statistics.ts | 6 +++ src/app/services/project.service.ts | 4 ++ 6 files changed, 57 insertions(+) create mode 100644 src/app/components/topology-summary/topology-summary.component.html create mode 100644 src/app/components/topology-summary/topology-summary.component.scss create mode 100644 src/app/components/topology-summary/topology-summary.component.spec.ts create mode 100644 src/app/components/topology-summary/topology-summary.component.ts create mode 100644 src/app/models/project-statistics.ts diff --git a/src/app/components/topology-summary/topology-summary.component.html b/src/app/components/topology-summary/topology-summary.component.html new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/topology-summary/topology-summary.component.scss b/src/app/components/topology-summary/topology-summary.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/topology-summary/topology-summary.component.spec.ts b/src/app/components/topology-summary/topology-summary.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/topology-summary/topology-summary.component.ts b/src/app/components/topology-summary/topology-summary.component.ts new file mode 100644 index 00000000..e62673f8 --- /dev/null +++ b/src/app/components/topology-summary/topology-summary.component.ts @@ -0,0 +1,47 @@ +import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { Project } from '../../models/project'; +import { Server } from '../../models/server'; +import { NodesDataSource } from '../../cartography/datasources/nodes-datasource'; +import { NodeService } from '../../services/node.service'; +import { Node } from '../../cartography/models/node'; +import { Subscription } from 'rxjs'; +import { ProjectService } from '../../services/project.service'; +import { ProjectStatistics } from '../../models/project-statistics'; + + +@Component({ + selector: 'app-topology-summary', + templateUrl: './topology-summary.component.html', + styleUrls: ['./topology-summary.component.scss'] +}) +export class TopologySummaryComponent implements OnInit, OnDestroy { + @Input() project: Project; + @Input() server: Server; + + private subscriptions: Subscription[]; + projectsStatistics: ProjectStatistics; + nodes: Node[] = []; + + //filters to introduce -> should be generic + + constructor( + private nodesDataSource: NodesDataSource, + private projectService: ProjectService + ) {} + + ngOnInit() { + this.subscriptions.push( + this.nodesDataSource.changes.subscribe((nodes: Node[]) => { + this.nodes = nodes; + }) + ); + + this.projectService.getStatistics(this.server, this.project.project_id).subscribe((stats: ProjectStatistics) => { + this.projectsStatistics = stats; + }); + } + + ngOnDestroy() { + this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); + } +} diff --git a/src/app/models/project-statistics.ts b/src/app/models/project-statistics.ts new file mode 100644 index 00000000..c26566ce --- /dev/null +++ b/src/app/models/project-statistics.ts @@ -0,0 +1,6 @@ +export class ProjectStatistics { + drawings: number; + links: number; + nodes: number; + snapshots: number +} diff --git a/src/app/services/project.service.ts b/src/app/services/project.service.ts index 4ca87f98..637dd624 100644 --- a/src/app/services/project.service.ts +++ b/src/app/services/project.service.ts @@ -49,6 +49,10 @@ export class ProjectService { return this.httpServer.delete(server, `/projects/${project_id}`); } + getStatistics(server: Server, project_id: string) { + return this.httpServer.get(server, `/projects/${project_id}/stats`); + } + notificationsPath(server: Server, project_id: string): string { return `ws://${server.host}:${server.port}/v2/projects/${project_id}/notifications/ws`; } From 909e197b9aa795e03ba63d1d026ea6ed4f875d02 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Wed, 14 Aug 2019 06:10:41 -0700 Subject: [PATCH 06/11] Filters added --- src/app/app.module.ts | 4 +- .../project-map/project-map.component.html | 1 + .../topology-summary.component.html | 53 +++++++++++++++ .../topology-summary.component.scss | 67 +++++++++++++++++++ .../topology-summary.component.ts | 61 +++++++++++++++-- src/app/services/project.service.ts | 2 +- 6 files changed, 179 insertions(+), 9 deletions(-) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4242bce2..852ee1cc 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -191,6 +191,7 @@ import { DuplicateActionComponent } from './components/project-map/context-menu/ import { MapSettingService } from './services/mapsettings.service'; import { ProjectMapMenuComponent } from './components/project-map/project-map-menu/project-map-menu.component'; import { HelpComponent } from './components/help/help.component'; +import { TopologySummaryComponent } from './components/topology-summary/topology-summary.component'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -312,7 +313,8 @@ if (environment.production) { ConsoleComponent, NodesMenuComponent, ProjectMapMenuComponent, - HelpComponent + HelpComponent, + TopologySummaryComponent ], imports: [ BrowserModule, diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index 42cbcd30..b0f64d5e 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -120,3 +120,4 @@ + diff --git a/src/app/components/topology-summary/topology-summary.component.html b/src/app/components/topology-summary/topology-summary.component.html index e69de29b..f4ee0470 100644 --- a/src/app/components/topology-summary/topology-summary.component.html +++ b/src/app/components/topology-summary/topology-summary.component.html @@ -0,0 +1,53 @@ +
+
+ Topology summary ({{projectsStatistics.snapshots}} snapshots) + close +
+ +
+ Filters
+ Running state +
+
+ Sorting
+
+ + By name ascending
+ By name descending +
+
+
+ +
+
+
+ + + + + + + {{node.name}} +
+
+ {{node.console_type}} {{node.console_host}}:{{node.console}} +
+
+ +
+
diff --git a/src/app/components/topology-summary/topology-summary.component.scss b/src/app/components/topology-summary/topology-summary.component.scss index e69de29b..9840d4bc 100644 --- a/src/app/components/topology-summary/topology-summary.component.scss +++ b/src/app/components/topology-summary/topology-summary.component.scss @@ -0,0 +1,67 @@ +.summaryWrapper { + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); + position: fixed; + top: 20px; + right: 20px; + height: 400px; + width: 300px; + background: #263238; + color: white; + overflow: hidden; + font-size: 12px; +} + +.summaryHeaderMenu { + height: 24px; +} + +.summaryHeader { + width: 100%; + height: 24px; + display: flex; + justify-content: space-between; + margin-right: 5px; +} + +.summaryFilters { + margin-left: 5px; + margin-right: 5px; +} + +.summarySorting { + margin-left: 5px; + margin-right: 5px; +} + +.summaryContent { + margin-left: 5px; + margin-right: 5px; +} + +.title { + margin-left: 5px; + margin-top: 4px; +} + +.divider { + margin: 5px; + width: 290px; + height: 2px; +} + +.nodeRow { + width: 100%; + display: flex; + justify-content: space-between; +} + +mat-icon { + font-size: 18px; + width: 20px; + height: 20px; + margin-top: 4px; +} + +.radio-group { + margin-top: 5px; +} diff --git a/src/app/components/topology-summary/topology-summary.component.ts b/src/app/components/topology-summary/topology-summary.component.ts index e62673f8..bec42d2c 100644 --- a/src/app/components/topology-summary/topology-summary.component.ts +++ b/src/app/components/topology-summary/topology-summary.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, Input } from '@angular/core'; +import { Component, OnInit, OnDestroy, Input, AfterViewInit } from '@angular/core'; import { Project } from '../../models/project'; import { Server } from '../../models/server'; import { NodesDataSource } from '../../cartography/datasources/nodes-datasource'; @@ -15,33 +15,80 @@ import { ProjectStatistics } from '../../models/project-statistics'; styleUrls: ['./topology-summary.component.scss'] }) export class TopologySummaryComponent implements OnInit, OnDestroy { - @Input() project: Project; @Input() server: Server; + @Input() project: Project; - private subscriptions: Subscription[]; + private subscriptions: Subscription[] = []; projectsStatistics: ProjectStatistics; nodes: Node[] = []; - - //filters to introduce -> should be generic + filteredNodes: Node[] = []; + dataSource: Node[] = []; + displayedColumns: string[] = ['name', 'console']; + sortingOrder: string = 'asc'; + statusFilterEnabled: boolean = false; constructor( private nodesDataSource: NodesDataSource, - private projectService: ProjectService + private projectService: ProjectService, + private nodeService: NodeService ) {} ngOnInit() { this.subscriptions.push( this.nodesDataSource.changes.subscribe((nodes: Node[]) => { this.nodes = nodes; + if (this.sortingOrder === 'asc') { + this.filteredNodes = nodes.sort(this.compareAsc); + } else { + this.filteredNodes = nodes.sort(this.compareDesc); + } }) ); - this.projectService.getStatistics(this.server, this.project.project_id).subscribe((stats: ProjectStatistics) => { + this.projectService.getStatistics(this.server, this.project.project_id).subscribe((stats) => { this.projectsStatistics = stats; }); } + compareAsc(first: Node, second: Node) { + if (first.name < second.name) return -1; + return 1; + } + + compareDesc(first: Node, second: Node) { + if (first.name < second.name) return 1; + return -1; + } + ngOnDestroy() { this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); } + + setSortingOrder(order: string) { + this.sortingOrder = order; + if (this.sortingOrder === 'asc') { + this.filteredNodes = this.filteredNodes.sort(this.compareAsc); + } else { + this.filteredNodes = this.filteredNodes.sort(this.compareDesc); + } + } + + applyStatusFilter(value: boolean) { + this.statusFilterEnabled = value; + this.applyFilters(); + } + + applyFilters() { + var nodes = this.nodes; + + if (this.statusFilterEnabled) { + nodes = nodes.filter(n => n.status === 'started'); + } + + if (this.sortingOrder === 'asc') { + this.filteredNodes = nodes.sort(this.compareAsc); + } else { + this.filteredNodes = nodes.sort(this.compareDesc); + } + } } diff --git a/src/app/services/project.service.ts b/src/app/services/project.service.ts index 637dd624..403cf9b0 100644 --- a/src/app/services/project.service.ts +++ b/src/app/services/project.service.ts @@ -49,7 +49,7 @@ export class ProjectService { return this.httpServer.delete(server, `/projects/${project_id}`); } - getStatistics(server: Server, project_id: string) { + getStatistics(server: Server, project_id: string): Observable { return this.httpServer.get(server, `/projects/${project_id}/stats`); } From 51ef91f8d36de186e605f614a59de38b368d859d Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Mon, 19 Aug 2019 00:59:04 -0700 Subject: [PATCH 07/11] Update topology summary component --- .../components/project-map/project-map.component.html | 9 +++++++-- .../components/project-map/project-map.component.scss | 4 ++++ src/app/components/project-map/project-map.component.ts | 5 +++++ .../topology-summary/topology-summary.component.html | 2 +- .../topology-summary/topology-summary.component.scss | 4 ++++ .../topology-summary/topology-summary.component.ts | 8 +++++++- 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index b0f64d5e..241abc4b 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -49,7 +49,7 @@ - + @@ -57,6 +57,9 @@ Show interface labels + + Show topology summary +
@@ -120,4 +123,6 @@ - +
+ +
diff --git a/src/app/components/project-map/project-map.component.scss b/src/app/components/project-map/project-map.component.scss index 49f1556a..553b5a13 100644 --- a/src/app/components/project-map/project-map.component.scss +++ b/src/app/components/project-map/project-map.component.scss @@ -230,3 +230,7 @@ g.node text, .context-menu-items .mat-menu-item:focus { background: none; } + +.visible { + display: none; +} diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 002d167b..20f6f1f3 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -68,6 +68,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { public server: Server; public ws: WebSocket; public isProjectMapMenuVisible: boolean = false; + public isTopologySummaryVisible: boolean = false; tools = { selection: true, @@ -362,6 +363,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.project.show_interface_labels = enabled; } + public toggleShowTopologySummary(visible: boolean) { + this.isTopologySummaryVisible = visible; + } + public hideMenu() { this.projectMapMenuComponent.resetDrawToolChoice() this.isProjectMapMenuVisible = false; diff --git a/src/app/components/topology-summary/topology-summary.component.html b/src/app/components/topology-summary/topology-summary.component.html index f4ee0470..f08c0dd6 100644 --- a/src/app/components/topology-summary/topology-summary.component.html +++ b/src/app/components/topology-summary/topology-summary.component.html @@ -1,7 +1,7 @@
Topology summary ({{projectsStatistics.snapshots}} snapshots) - close + close
diff --git a/src/app/components/topology-summary/topology-summary.component.scss b/src/app/components/topology-summary/topology-summary.component.scss index 9840d4bc..63e25338 100644 --- a/src/app/components/topology-summary/topology-summary.component.scss +++ b/src/app/components/topology-summary/topology-summary.component.scss @@ -65,3 +65,7 @@ mat-icon { .radio-group { margin-top: 5px; } + +.closeButton { + cursor: pointer; +} diff --git a/src/app/components/topology-summary/topology-summary.component.ts b/src/app/components/topology-summary/topology-summary.component.ts index bec42d2c..f758af53 100644 --- a/src/app/components/topology-summary/topology-summary.component.ts +++ b/src/app/components/topology-summary/topology-summary.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, OnDestroy, Input, AfterViewInit } from '@angular/core'; +import { Component, OnInit, OnDestroy, Input, AfterViewInit, Output, EventEmitter } from '@angular/core'; import { Project } from '../../models/project'; import { Server } from '../../models/server'; import { NodesDataSource } from '../../cartography/datasources/nodes-datasource'; @@ -18,6 +18,8 @@ export class TopologySummaryComponent implements OnInit, OnDestroy { @Input() server: Server; @Input() project: Project; + @Output() closeTopologySummary = new EventEmitter(); + private subscriptions: Subscription[] = []; projectsStatistics: ProjectStatistics; nodes: Node[] = []; @@ -91,4 +93,8 @@ export class TopologySummaryComponent implements OnInit, OnDestroy { this.filteredNodes = nodes.sort(this.compareDesc); } } + + close() { + this.closeTopologySummary.emit(false); + } } From 0524099dce0c760ba5c884a0568cc095829f31f6 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Tue, 20 Aug 2019 04:44:14 -0700 Subject: [PATCH 08/11] Update info-dialog-component --- .../project-map/info-dialog/info-dialog.component.html | 2 +- src/app/services/info.service.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/app/components/project-map/info-dialog/info-dialog.component.html b/src/app/components/project-map/info-dialog/info-dialog.component.html index 96a2234a..d178a63e 100644 --- a/src/app/components/project-map/info-dialog/info-dialog.component.html +++ b/src/app/components/project-map/info-dialog/info-dialog.component.html @@ -14,7 +14,7 @@ Please start the node in order to get the command line information.
- {{command_line}} + {{commandLine}}
diff --git a/src/app/services/info.service.ts b/src/app/services/info.service.ts index 0e722927..ed3860f4 100644 --- a/src/app/services/info.service.ts +++ b/src/app/services/info.service.ts @@ -56,7 +56,15 @@ export class InfoService { } getCommandLine(node: Node): string { - if (node.node_type === 'cloud' || "nat" || "ethernet_hub" || "ethernet_switch" || "frame_relay_switch" || "atm_switch" || "dynamips" || "traceng" || "iou") { + if (node.node_type === "cloud" || + node.node_type === "nat" || + node.node_type === "ethernet_hub" || + node.node_type === "ethernet_switch" || + node.node_type === "frame_relay_switch" || + node.node_type === "atm_switch" || + node.node_type === "dynamips" || + node.node_type === "traceng" || + node.node_type === "iou") { return 'Command line information is not supported for this type of node.'; } else { if (node.status === 'started') { From 8163b4d70961c4fcad7cd53daf335b7ac7264f17 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Tue, 20 Aug 2019 05:49:54 -0700 Subject: [PATCH 09/11] Unit tests added --- .../projects/projects.component.html | 2 +- .../projects/projects.component.spec.ts | 22 +++++++++++++++++++ src/app/services/project.service.spec.ts | 4 ++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/app/components/projects/projects.component.html b/src/app/components/projects/projects.component.html index 107c070b..90d28c2e 100644 --- a/src/app/components/projects/projects.component.html +++ b/src/app/components/projects/projects.component.html @@ -40,7 +40,7 @@ -
- Filters
- Running state + Filter by status
+
+ Started + Suspended + Stopped +
Sorting
@@ -29,9 +33,12 @@ {{node.name}}
-
+
{{node.console_type}} {{node.console_host}}:{{node.console}}
+
+ none +
diff --git a/src/app/components/topology-summary/topology-summary.component.scss b/src/app/components/topology-summary/topology-summary.component.scss index f51f82ab..a9629c20 100644 --- a/src/app/components/topology-summary/topology-summary.component.scss +++ b/src/app/components/topology-summary/topology-summary.component.scss @@ -36,7 +36,7 @@ .summaryContent { margin-left: 5px; margin-right: 5px; - max-height: 220px; + max-height: 240px; overflow: auto; scrollbar-color: darkgrey #263238; scrollbar-width: thin; @@ -80,10 +80,15 @@ mat-icon { outline: 1px solid #263238; } -.radio-group { +.radio-group-wrapper { margin-top: 5px; } +.radio-group { + display: flex; + justify-content: space-between; +} + .closeButton { cursor: pointer; } diff --git a/src/app/components/topology-summary/topology-summary.component.spec.ts b/src/app/components/topology-summary/topology-summary.component.spec.ts index 2119197d..3cbffe52 100644 --- a/src/app/components/topology-summary/topology-summary.component.spec.ts +++ b/src/app/components/topology-summary/topology-summary.component.spec.ts @@ -13,7 +13,7 @@ import { Project } from '../../models/project'; import { Node } from '../../cartography/models/node'; -fdescribe('TopologySummaryComponent', () => { +describe('TopologySummaryComponent', () => { let component: TopologySummaryComponent; let fixture: ComponentFixture; let mockedProjectService: MockedProjectService = new MockedProjectService(); diff --git a/src/app/components/topology-summary/topology-summary.component.ts b/src/app/components/topology-summary/topology-summary.component.ts index 0170312c..a53402e1 100644 --- a/src/app/components/topology-summary/topology-summary.component.ts +++ b/src/app/components/topology-summary/topology-summary.component.ts @@ -23,8 +23,6 @@ export class TopologySummaryComponent implements OnInit, OnDestroy { projectsStatistics: ProjectStatistics; nodes: Node[] = []; filteredNodes: Node[] = []; - dataSource: Node[] = []; - displayedColumns: string[] = ['name', 'console']; sortingOrder: string = 'asc'; startedStatusFilterEnabled: boolean = false; suspendedStatusFilterEnabled: boolean = false; diff --git a/src/app/services/mapsettings.service.ts b/src/app/services/mapsettings.service.ts index 640c5671..caf4a6c8 100644 --- a/src/app/services/mapsettings.service.ts +++ b/src/app/services/mapsettings.service.ts @@ -2,12 +2,17 @@ import { Injectable } from "@angular/core"; import { Subject } from 'rxjs'; @Injectable() -export class MapSettingService { +export class MapSettingsService { public isMapLocked = new Subject(); + public isTopologySummaryVisible: boolean = false; constructor() {} changeMapLockValue(value: boolean) { this.isMapLocked.next(value); } + + toggleTopologySummary(value: boolean) { + this.isTopologySummaryVisible = value; + } }