diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 9cfa01b1..683f4f88 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -6,7 +6,7 @@ import { ServersComponent } from './components/servers/servers.component'; import { ProjectsComponent } from './components/projects/projects.component'; import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component'; import { SettingsComponent } from './components/settings/settings.component'; -import { BundledServerFinderComponent } from './components/local-server/bundled-server-finder.component'; +import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component'; import { PreferencesComponent } from './components/preferences/preferences.component'; import { QemuPreferencesComponent } from './components/preferences/qemu/qemu-preferences/qemu-preferences.component'; import { QemuVmTemplatesComponent } from './components/preferences/qemu/qemu-vm-templates/qemu-vm-templates.component'; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d48aa042..eab5be9e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -58,7 +58,7 @@ import { ProjectMapShortcutsComponent } from './components/project-map/project-m import { SettingsComponent } from './components/settings/settings.component'; import { SettingsService } from './services/settings.service'; -import { BundledServerFinderComponent } from './components/local-server/bundled-server-finder.component'; +import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component'; import { ProgressComponent } from './common/progress/progress.component'; import { ProgressService } from './common/progress/progress.service'; import { version } from './version'; @@ -169,6 +169,14 @@ import { DateFilter } from './filters/dateFilter.pipe'; import { NameFilter } from './filters/nameFilter.pipe'; import { CustomAdaptersComponent } from './components/preferences/common/custom-adapters/custom-adapters.component'; import { NodesMenuComponent } from './components/project-map/nodes-menu/nodes-menu.component'; +import { PacketFiltersActionComponent } from './components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component'; +import { PacketFiltersDialogComponent } from './components/project-map/packet-capturing/packet-filters/packet-filters.component'; +import { HelpDialogComponent } from './components/project-map/help-dialog/help-dialog.component'; +import { StartCaptureActionComponent } from './components/project-map/context-menu/actions/start-capture/start-capture-action.component'; +import { StartCaptureDialogComponent } from './components/project-map/packet-capturing/start-capture/start-capture.component'; +import { SuspendLinkActionComponent } from './components/project-map/context-menu/actions/suspend-link/suspend-link-action.component'; +import { ResumeLinkActionComponent } from './components/project-map/context-menu/actions/resume-link-action/resume-link-action.component'; +import { StopCaptureActionComponent } from './components/project-map/context-menu/actions/stop-capture/stop-capture-action.component'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -203,6 +211,11 @@ if (environment.production) { EditStyleActionComponent, EditTextActionComponent, DeleteActionComponent, + PacketFiltersActionComponent, + StartCaptureActionComponent, + StopCaptureActionComponent, + ResumeLinkActionComponent, + SuspendLinkActionComponent, ProjectMapShortcutsComponent, SettingsComponent, PreferencesComponent, @@ -224,6 +237,7 @@ if (environment.production) { InstallSoftwareComponent, StyleEditorDialogComponent, TextEditorDialogComponent, + PacketFiltersDialogComponent, QemuPreferencesComponent, QemuVmTemplatesComponent, AddQemuVmTemplateComponent, @@ -257,6 +271,8 @@ if (environment.production) { VmwareTemplateDetailsComponent, AddVmwareTemplateComponent, DeleteConfirmationDialogComponent, + HelpDialogComponent, + StartCaptureDialogComponent, DeleteTemplateComponent, DockerTemplatesComponent, AddDockerTemplateComponent, @@ -355,9 +371,12 @@ if (environment.production) { ImportProjectDialogComponent, ConfirmationDialogComponent, StyleEditorDialogComponent, + PacketFiltersDialogComponent, TextEditorDialogComponent, SymbolsComponent, - DeleteConfirmationDialogComponent + DeleteConfirmationDialogComponent, + HelpDialogComponent, + StartCaptureDialogComponent ], bootstrap: [AppComponent] }) diff --git a/src/app/cartography/converters/map/link-to-map-link-converter.ts b/src/app/cartography/converters/map/link-to-map-link-converter.ts index c2946f08..b9afd052 100644 --- a/src/app/cartography/converters/map/link-to-map-link-converter.ts +++ b/src/app/cartography/converters/map/link-to-map-link-converter.ts @@ -15,9 +15,11 @@ export class LinkToMapLinkConverter implements Converter { mapLink.captureFileName = link.capture_file_name; mapLink.captureFilePath = link.capture_file_path; mapLink.capturing = link.capturing; + mapLink.filters = link.filters; mapLink.linkType = link.link_type; mapLink.nodes = link.nodes.map(linkNode => this.linkNodeToMapLinkNode.convert(linkNode, { link_id: link.link_id })); mapLink.projectId = link.project_id; + mapLink.suspend = link.suspend; return mapLink; } } diff --git a/src/app/cartography/converters/map/map-link-to-link-converter.ts b/src/app/cartography/converters/map/map-link-to-link-converter.ts index e7db39dc..857ac394 100644 --- a/src/app/cartography/converters/map/map-link-to-link-converter.ts +++ b/src/app/cartography/converters/map/map-link-to-link-converter.ts @@ -15,9 +15,11 @@ export class MapLinkToLinkConverter implements Converter { link.capture_file_name = mapLink.captureFileName; link.capture_file_path = mapLink.captureFilePath; link.capturing = mapLink.capturing; + link.filters = mapLink.filters; link.link_type = mapLink.linkType; link.nodes = mapLink.nodes.map(mapLinkNode => this.mapLinkNodeToMapLinkNode.convert(mapLinkNode)); link.project_id = mapLink.projectId; + link.suspend = mapLink.suspend; return link; } } diff --git a/src/app/cartography/events/event-source.ts b/src/app/cartography/events/event-source.ts index a54e4d5d..7252129a 100644 --- a/src/app/cartography/events/event-source.ts +++ b/src/app/cartography/events/event-source.ts @@ -1,5 +1,6 @@ import { TextElement } from '../models/drawings/text-element'; import { MapDrawing } from '../models/map/map-drawing'; +import { MapLink } from '../models/map/map-link'; export class DataEventSource { constructor(public datum: T, public dx: number, public dy: number) {} @@ -30,3 +31,7 @@ export class TextEditedDataEvent { export class DrawingContextMenu { constructor(public event: any, public drawing: MapDrawing) {} } + +export class LinkContextMenu { + constructor(public event:any, public link: MapLink) {} +} diff --git a/src/app/cartography/models/map/map-link.ts b/src/app/cartography/models/map/map-link.ts index 0a0fb3a8..176d4ab1 100644 --- a/src/app/cartography/models/map/map-link.ts +++ b/src/app/cartography/models/map/map-link.ts @@ -1,15 +1,18 @@ import { MapLinkNode } from './map-link-node'; import { MapNode } from './map-node'; import { Indexed } from '../../datasources/map-datasource'; +import { Filter } from '../../../models/filter'; export class MapLink implements Indexed { id: string; captureFileName: string; captureFilePath: string; capturing: boolean; + filters?: Filter; linkType: string; nodes: MapLinkNode[]; projectId: string; + suspend: boolean; distance: number; // this is not from server length: number; // this is not from server diff --git a/src/app/cartography/widgets/link.ts b/src/app/cartography/widgets/link.ts index 1c88c5ec..5874574a 100644 --- a/src/app/cartography/widgets/link.ts +++ b/src/app/cartography/widgets/link.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, EventEmitter } from '@angular/core'; import { Widget } from './widget'; import { SVGSelection } from '../models/types'; @@ -9,9 +9,13 @@ import { InterfaceLabelWidget } from './interface-label'; import { InterfaceStatusWidget } from './interface-status'; import { MapLink } from '../models/map/map-link'; import { SelectionManager } from '../managers/selection-manager'; +import { event } from 'd3-selection'; +import { LinkContextMenu } from '../events/event-source'; @Injectable() export class LinkWidget implements Widget { + public onContextMenu = new EventEmitter(); + constructor( private multiLinkCalculatorHelper: MultiLinkCalculatorHelper, private interfaceLabelWidget: InterfaceLabelWidget, @@ -32,6 +36,58 @@ export class LinkWidget implements Widget { return `translate (${translation.dx}, ${translation.dy})`; }); + link_body.select('.capture-icon').remove(); + link_body + .filter(l => { return l.capturing && !(l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)}) + .append('g') + .on('contextmenu', (datum: MapLink) => { + const evt = event; + this.onContextMenu.emit(new LinkContextMenu(evt, datum)); + }) + .attr('class', 'capture-icon') + .attr('transform', link => { + return `translate (${(link.source.x + link.target.x)/2 + 24}, ${(link.source.y + link.target.y)/2 + 24}) scale(0.5)` + }) + .attr('viewBox', '0 0 20 20') + .append('image') + .attr("xlink:href", "assets/resources/images/inspect.svg"); + + link_body.select('.filter-capture-icon').remove(); + link_body + .filter(l => { return l.capturing && (l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)}) + .append('g') + .on('contextmenu', (datum: MapLink) => { + const evt = event; + this.onContextMenu.emit(new LinkContextMenu(evt, datum)); + }) + .attr('class', 'filter-capture-icon') + .attr('transform', link => { + return `translate (${(link.source.x + link.target.x)/2 + 24}, ${(link.source.y + link.target.y)/2 + 24}) scale(0.5)` + }) + .attr('viewBox', '0 0 20 20') + .append('image') + .attr("xlink:href", "assets/resources/images/filter-capture.svg"); + + link_body.select('.filter-icon').remove(); + link_body + .filter(l => { return !l.capturing && (l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)}) + .append('g') + .on('contextmenu', (datum: MapLink) => { + const evt = event; + this.onContextMenu.emit(new LinkContextMenu(evt, datum)); + }) + .attr('class', 'filter-icon') + .attr('width', '48px') + .attr('height', '48px') + .attr('transform', link => { + return `translate (${(link.source.x + link.target.x)/2 + 24}, ${(link.source.y + link.target.y)/2 + 24}) scale(0.5)` + }) + .attr('viewBox', '0 0 20 20') + .append('image') + .attr('width', '48px') + .attr('height', '48px') + .attr("xlink:href", "assets/resources/images/filter.svg"); + const serial_link_widget = new SerialLinkWidget(); serial_link_widget.draw(link_body_merge); diff --git a/src/app/components/local-server/bundled-server-finder.component.html b/src/app/components/bundled-server-finder/bundled-server-finder.component.html similarity index 100% rename from src/app/components/local-server/bundled-server-finder.component.html rename to src/app/components/bundled-server-finder/bundled-server-finder.component.html diff --git a/src/app/components/local-server/bundled-server-finder.component.scss b/src/app/components/bundled-server-finder/bundled-server-finder.component.scss similarity index 100% rename from src/app/components/local-server/bundled-server-finder.component.scss rename to src/app/components/bundled-server-finder/bundled-server-finder.component.scss diff --git a/src/app/components/local-server/bundled-server-finder.component.spec.ts b/src/app/components/bundled-server-finder/bundled-server-finder.component.spec.ts similarity index 100% rename from src/app/components/local-server/bundled-server-finder.component.spec.ts rename to src/app/components/bundled-server-finder/bundled-server-finder.component.spec.ts diff --git a/src/app/components/local-server/bundled-server-finder.component.ts b/src/app/components/bundled-server-finder/bundled-server-finder.component.ts similarity index 100% rename from src/app/components/local-server/bundled-server-finder.component.ts rename to src/app/components/bundled-server-finder/bundled-server-finder.component.ts diff --git a/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.html b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.html new file mode 100644 index 00000000..ea808178 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.ts b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.ts new file mode 100644 index 00000000..54dca3e0 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component.ts @@ -0,0 +1,30 @@ +import { Component, Input } from "@angular/core"; +import { Link } from '../../../../../models/link'; +import { Server } from '../../../../../models/server'; +import { Project } from '../../../../../models/project'; +import { MatDialog } from '@angular/material'; +import { PacketFiltersDialogComponent } from '../../../packet-capturing/packet-filters/packet-filters.component'; + +@Component({ + selector: 'app-packet-filters-action', + templateUrl: './packet-filters-action.component.html' +}) +export class PacketFiltersActionComponent { + @Input() server: Server; + @Input() project: Project; + @Input() link: Link; + + constructor(private dialog: MatDialog) {} + + openPacketFilters() { + const dialogRef = this.dialog.open(PacketFiltersDialogComponent, { + width: '900px', + height: '400px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + instance.project = this.project; + instance.link = this.link; + } +} diff --git a/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.html b/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.html new file mode 100644 index 00000000..ffc7be94 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.ts b/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.ts new file mode 100644 index 00000000..9cbb48bb --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/resume-link-action/resume-link-action.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from '@angular/core'; +import { Server } from '../../../../../models/server'; +import { Link } from '../../../../../models/link'; +import { LinkService } from '../../../../../services/link.service'; + +@Component({ + selector: 'app-resume-link-action', + templateUrl: './resume-link-action.component.html' +}) +export class ResumeLinkActionComponent { + @Input() server: Server; + @Input() link: Link; + + constructor( + private linkService: LinkService + ) {} + + resumeLink() { + this.link.suspend = false; + this.linkService.updateLink(this.server, this.link).subscribe(() => {}); + } +} diff --git a/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.html b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.html new file mode 100644 index 00000000..551336cf --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.ts b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.ts new file mode 100644 index 00000000..726e42bf --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/start-capture/start-capture-action.component.ts @@ -0,0 +1,26 @@ +import { Component, Input } from '@angular/core'; +import { Server } from '../../../../../models/server'; +import { Link } from '../../../../../models/link'; +import { MatDialog } from '@angular/material'; +import { StartCaptureDialogComponent } from '../../../packet-capturing/start-capture/start-capture.component'; + +@Component({ + selector: 'app-start-capture-action', + templateUrl: './start-capture-action.component.html' +}) +export class StartCaptureActionComponent { + @Input() server: Server; + @Input() link: Link; + + constructor(private dialog: MatDialog) {} + + startCapture() { + const dialogRef = this.dialog.open(StartCaptureDialogComponent, { + width: '400px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.server = this.server; + instance.link = this.link; + } +} diff --git a/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.html b/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.html new file mode 100644 index 00000000..68dd9851 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.ts b/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.ts new file mode 100644 index 00000000..3dfcaeab --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/stop-capture/stop-capture-action.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from '@angular/core'; +import { Server } from '../../../../../models/server'; +import { Link } from '../../../../../models/link'; +import { LinkService } from '../../../../../services/link.service'; + +@Component({ + selector: 'app-stop-capture-action', + templateUrl: './stop-capture-action.component.html' +}) +export class StopCaptureActionComponent { + @Input() server: Server; + @Input() link: Link; + + constructor( + private linkService: LinkService + ) {} + + stopCapture() { + this.linkService.stopCaptureOnLink(this.server, this.link).subscribe(() => {}); + } +} diff --git a/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.html b/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.html new file mode 100644 index 00000000..c503dadb --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.ts b/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.ts new file mode 100644 index 00000000..f07dc592 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/suspend-link/suspend-link-action.component.ts @@ -0,0 +1,22 @@ +import { Component, Input } from '@angular/core'; +import { Server } from '../../../../../models/server'; +import { Link } from '../../../../../models/link'; +import { LinkService } from '../../../../../services/link.service'; + +@Component({ + selector: 'app-suspend-link-action', + templateUrl: './suspend-link-action.component.html' +}) +export class SuspendLinkActionComponent { + @Input() server: Server; + @Input() link: Link; + + constructor( + private linkService: LinkService + ) {} + + suspendLink() { + this.link.suspend = true; + this.linkService.updateLink(this.server, this.link).subscribe(() => {}); + } +} 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 0060c9f1..8d778210 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 @@ -26,6 +26,34 @@ [nodes]="nodes" [drawings]="drawings" > + + + + + { let component: ContextMenuComponent; @@ -28,6 +29,7 @@ describe('ContextMenuComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ContextMenuComponent); component = fixture.componentInstance; + component.server = {location: 'local'} as Server; fixture.detectChanges(); }); diff --git a/src/app/components/project-map/context-menu/context-menu.component.ts b/src/app/components/project-map/context-menu/context-menu.component.ts index ed34f146..ef4e30b8 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.ts +++ b/src/app/components/project-map/context-menu/context-menu.component.ts @@ -31,6 +31,7 @@ export class ContextMenuComponent implements OnInit { links: Link[] = []; hasTextCapabilities: boolean = false; + isBundledServer: boolean = false; constructor( private sanitizer: DomSanitizer, @@ -40,6 +41,7 @@ export class ContextMenuComponent implements OnInit { ngOnInit() { this.setPosition(0, 0); + this.isBundledServer = this.server.location === 'bundled'; } public setPosition(top: number, left: number) { diff --git a/src/app/components/project-map/help-dialog/help-dialog.component.html b/src/app/components/project-map/help-dialog/help-dialog.component.html new file mode 100644 index 00000000..0f96d635 --- /dev/null +++ b/src/app/components/project-map/help-dialog/help-dialog.component.html @@ -0,0 +1,16 @@ +

{{title}}

+ + + +
+ +
diff --git a/src/app/components/project-map/help-dialog/help-dialog.component.scss b/src/app/components/project-map/help-dialog/help-dialog.component.scss new file mode 100644 index 00000000..f382464c --- /dev/null +++ b/src/app/components/project-map/help-dialog/help-dialog.component.scss @@ -0,0 +1,7 @@ +.message { + margin-bottom: 10px; +} + +.description { + color: #b0bec5; +} diff --git a/src/app/components/project-map/help-dialog/help-dialog.component.spec.ts b/src/app/components/project-map/help-dialog/help-dialog.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/help-dialog/help-dialog.component.ts b/src/app/components/project-map/help-dialog/help-dialog.component.ts new file mode 100644 index 00000000..7145c03c --- /dev/null +++ b/src/app/components/project-map/help-dialog/help-dialog.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from '@angular/core'; +import { MatDialogRef } from '@angular/material'; +import { Message } from '../../../models/message'; + +@Component({ + selector: 'app-help-dialog', + templateUrl: './help-dialog.component.html', + styleUrls: ['./help-dialog.component.scss'] +}) +export class HelpDialogComponent { + @Input() title: string; + @Input() messages: Message[]; + + constructor( + public dialogRef: MatDialogRef, + ) {} + + onCloseClick() { + this.dialogRef.close(); + } +} diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.html b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.html new file mode 100644 index 00000000..09856747 --- /dev/null +++ b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.html @@ -0,0 +1,44 @@ +

Packet filters

+ + +
+
+
+ + + +
+ +
+
diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.scss b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.scss new file mode 100644 index 00000000..529a458e --- /dev/null +++ b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.scss @@ -0,0 +1,67 @@ +.spacer { + flex-grow: 1; +} + +.content { + height: 260px; +} + +.item { + height: 25px; + font-size: 10pt; + margin-bottom: 10px; +} + +.item-name { + margin-bottom: 10px; +} + +.item-value { + width: 100%; + margin-bottom: 10px; +} + +.input-field { + width: 100%; + margin-top: 10px; +} + +.divider { + width: fit-content; + flex: 1 1 auto; +} + +.input-color { + padding: 0px; + border-width: 0px; + width: 100%; + background-color: transparent; + outline: none; +} + +input:focus { + outline: none; +} + +input[type="color"] { + -webkit-appearance: none; + border: none; + height: 25px; +} + +input[type="color"]::-webkit-color-swatch-wrapper { + padding: 0; +} + +input[type="color"]::-webkit-color-swatch { + border: none; +} + +.modal-form-container { + display: flex; + flex-direction: column; +} + +.modal-form-container > * { + width: 100%; +} diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.spec.ts b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.spec.ts new file mode 100644 index 00000000..257aa33e --- /dev/null +++ b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.spec.ts @@ -0,0 +1,63 @@ +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { CommonModule } from '@angular/common'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { LinkService } from '../../../../services/link.service'; +import { MockedLinkService } from '../../project-map.component.spec'; +import { Link } from '../../../../models/link'; +import { of } from 'rxjs'; +import { PacketFiltersDialogComponent } from './packet-filters.component'; + +describe('PacketFiltersDialogComponent', () => { + let component: PacketFiltersDialogComponent; + let fixture: ComponentFixture; + + let mockedLinkService = new MockedLinkService; + let dialogRef = { + close: jasmine.createSpy('close') + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [MatDialogModule, FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule], + providers: [ + { provide: MatDialogRef, useValue: dialogRef }, + { provide: MAT_DIALOG_DATA, useValue: [] }, + { provide: LinkService, useValue: mockedLinkService } + ], + declarations: [ + PacketFiltersDialogComponent + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PacketFiltersDialogComponent); + component = fixture.componentInstance; + component.link = {link_type: 'ethernet'} as Link; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call update link when filters applied', () => { + spyOn(mockedLinkService, 'updateLink').and.returnValue(of({})); + + component.onYesClick(); + + expect(mockedLinkService.updateLink).toHaveBeenCalled(); + }); + + it('should call update link after resetting', () => { + spyOn(mockedLinkService, 'updateLink').and.returnValue(of({})); + + component.onResetClick(); + + expect(mockedLinkService.updateLink).toHaveBeenCalled(); + }); +}); diff --git a/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.ts b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.ts new file mode 100644 index 00000000..19cf6e7f --- /dev/null +++ b/src/app/components/project-map/packet-capturing/packet-filters/packet-filters.component.ts @@ -0,0 +1,96 @@ +import { Component, OnInit } from '@angular/core'; +import { Link } from '../../../../models/link'; +import { Server } from '../../../../models/server'; +import { Project } from '../../../../models/project'; +import { MatDialogRef, MatDialog } from '@angular/material'; +import { LinkService } from '../../../../services/link.service'; +import { FilterDescription } from '../../../../models/filter-description'; +import { HelpDialogComponent } from '../../help-dialog/help-dialog.component'; +import { Message } from '../../../../models/message'; +import { Filter } from '../../../../models/filter'; + +@Component({ + selector: 'app-packet-filters', + templateUrl: './packet-filters.component.html', + styleUrls: ['./packet-filters.component.scss'] +}) +export class PacketFiltersDialogComponent implements OnInit{ + server: Server; + project: Project; + link: Link; + filters: Filter; + availableFilters: FilterDescription[]; + + constructor( + private dialogRef: MatDialogRef, + private linkService: LinkService, + private dialog: MatDialog + ) {} + + ngOnInit(){ + this.linkService.getLink(this.server, this.link.project_id, this.link.link_id).subscribe((link: Link) => { + this.link = link; + this.filters = { + bpf: [], + corrupt: [0], + delay: [0, 0], + frequency_drop: [0], + packet_loss: [0] + }; + + if (this.link.filters) { + this.filters.bpf = this.link.filters.bpf ? this.link.filters.bpf : []; + this.filters.corrupt = this.link.filters.corrupt ? this.link.filters.corrupt : [0]; + this.filters.delay = this.link.filters.delay ? this.link.filters.delay : [0, 0]; + this.filters.frequency_drop = this.link.filters.frequency_drop ? this.link.filters.frequency_drop : [0]; + this.filters.packet_loss = this.link.filters.packet_loss ? this.link.filters.packet_loss : [0]; + } + }); + + this.linkService.getAvailableFilters(this.server, this.link).subscribe((availableFilters: FilterDescription[]) => { + this.availableFilters = availableFilters; + }); + } + + onNoClick() { + this.dialogRef.close(); + } + + onResetClick() { + this.link.filters = { + bpf: [], + corrupt: [0], + delay: [0, 0], + frequency_drop: [0], + packet_loss: [0] + }; + + this.linkService.updateLink(this.server, this.link).subscribe((link: Link) => { + this.dialogRef.close(); + }); + } + + onYesClick() { + this.link.filters = this.filters; + this.linkService.updateLink(this.server, this.link).subscribe((link: Link) => { + this.dialogRef.close(); + }); + } + + onHelpClick() { + const dialogRef = this.dialog.open(HelpDialogComponent, { + width: '500px', + autoFocus: false + }); + let instance = dialogRef.componentInstance; + instance.title = 'Help for filters'; + let messages: Message[] = []; + this.availableFilters.forEach((filter: FilterDescription) => { + messages.push({ + name: filter.name, + description: filter.description + }); + }); + instance.messages = messages; + } +} diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.html b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.html new file mode 100644 index 00000000..80ece941 --- /dev/null +++ b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.html @@ -0,0 +1,30 @@ +

Packet capture

+ + + +
+ + +
diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.scss b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.scss new file mode 100644 index 00000000..396495e3 --- /dev/null +++ b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.scss @@ -0,0 +1,3 @@ +.input-field { + width: 100%; +} diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts new file mode 100644 index 00000000..945caaee --- /dev/null +++ b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.spec.ts @@ -0,0 +1,82 @@ +import { StartCaptureDialogComponent } from "./start-capture.component"; +import { ComponentFixture, TestBed, async } from '@angular/core/testing'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { CommonModule } from '@angular/common'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ToasterService } from '../../../../services/toaster.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { MockedToasterService } from '../../../../services/toaster.service.spec'; +import { LinkService } from '../../../../services/link.service'; +import { MockedLinkService, MockedNodesDataSource } from '../../project-map.component.spec'; +import { Link } from '../../../../models/link'; +import { of } from 'rxjs'; +import { NodesDataSource } from '../../../../cartography/datasources/nodes-datasource'; + +describe('StartCaptureDialogComponent', () => { + let component: StartCaptureDialogComponent; + let fixture: ComponentFixture; + + let mockedToasterService = new MockedToasterService; + let mockedLinkService = new MockedLinkService; + let mockedNodesDataSource = new MockedNodesDataSource; + let dialogRef = { + close: jasmine.createSpy('close') + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [MatDialogModule, FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule], + providers: [ + { provide: MatDialogRef, useValue: dialogRef }, + { provide: MAT_DIALOG_DATA, useValue: [] }, + { provide: ToasterService, useValue: mockedToasterService }, + { provide: LinkService, useValue: mockedLinkService }, + { provide: NodesDataSource, useValue: mockedNodesDataSource } + ], + declarations: [ + StartCaptureDialogComponent + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(StartCaptureDialogComponent); + component = fixture.componentInstance; + component.link = {link_type: 'ethernet', nodes: [{node_id: '1'}, {node_id: '2'}]} as Link; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call link service when input is valid', () => { + component.inputForm.controls['linkType'].setValue('Ethernet'); + component.inputForm.controls['fileName'].setValue('SampleFileName'); + spyOn(mockedLinkService, 'startCaptureOnLink').and.returnValue(of({})); + + component.onYesClick(); + + expect(mockedLinkService.startCaptureOnLink).toHaveBeenCalled(); + }); + + it('should not call link service when link type is not set', () => { + component.inputForm.controls['fileName'].setValue('SampleFileName'); + spyOn(mockedLinkService, 'startCaptureOnLink').and.returnValue(of({})); + + component.onYesClick(); + + expect(mockedLinkService.startCaptureOnLink).not.toHaveBeenCalled(); + }); + + it('should not call link service when filename is empty', () => { + component.inputForm.controls['linkType'].setValue('Ethernet'); + component.inputForm.controls['fileName'].setValue(''); + spyOn(mockedLinkService, 'startCaptureOnLink').and.returnValue(of({})); + + component.onYesClick(); + + expect(mockedLinkService.startCaptureOnLink).not.toHaveBeenCalled(); + }); +}); diff --git a/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.ts b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.ts new file mode 100644 index 00000000..b9ef34c7 --- /dev/null +++ b/src/app/components/project-map/packet-capturing/start-capture/start-capture.component.ts @@ -0,0 +1,85 @@ +import { Component, OnInit } from '@angular/core'; +import { Server } from '../../../../models/server'; +import { Link } from '../../../../models/link'; +import { MatDialogRef } from '@angular/material'; +import { PacketFiltersDialogComponent } from '../packet-filters/packet-filters.component'; +import { LinkService } from '../../../../services/link.service'; +import { CapturingSettings } from '../../../../models/capturingSettings'; +import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms'; +import { ToasterService } from '../../../../services/toaster.service'; +import { LinkNode } from '../../../../models/link-node'; +import { NodesDataSource } from '../../../../cartography/datasources/nodes-datasource'; + +@Component({ + selector: 'app-start-capture', + templateUrl: './start-capture.component.html', + styleUrls: ['./start-capture.component.scss'] +}) +export class StartCaptureDialogComponent implements OnInit { + server: Server; + link: Link; + linkTypes = []; + inputForm: FormGroup; + startProgram: boolean; + + constructor( + private dialogRef: MatDialogRef, + private linkService: LinkService, + private formBuilder: FormBuilder, + private toasterService: ToasterService, + private nodesDataSource: NodesDataSource + ) { + this.inputForm = this.formBuilder.group({ + linkType: new FormControl('', Validators.required), + fileName: new FormControl('', Validators.required) + }); + } + + ngOnInit() { + if (this.link.link_type === 'ethernet') { + this.linkTypes = [ + ["Ethernet", "DLT_EN10MB"] + ]; + } else { + this.linkTypes = [ + ["Cisco HDLC", "DLT_C_HDLC"], + ["Cisco PPP", "DLT_PPP_SERIAL"], + ["Frame Relay", "DLT_FRELAY"], + ["ATM", "DLT_ATM_RFC1483"] + ]; + } + + const sourceNode = this.nodesDataSource.get(this.link.nodes[0].node_id); + const targetNode = this.nodesDataSource.get(this.link.nodes[1].node_id); + const sourcePort = sourceNode.ports[this.link.nodes[0].port_number]; + const targetPort = targetNode.ports[this.link.nodes[1].port_number]; + this.inputForm.controls['fileName'].setValue(`${sourceNode.name}_${sourcePort.name}_to_${targetNode.name}_${targetPort.name}`); + } + + onYesClick() { + let isAnyRunningDevice = false; + this.link.nodes.forEach((linkNode: LinkNode) => { + let node = this.nodesDataSource.get(linkNode.node_id); + if (node.status === 'started') isAnyRunningDevice = true; + }); + + if (!isAnyRunningDevice) { + this.toasterService.error(`Cannot capture because there is no running device on this link`); + } else if (this.inputForm.invalid) { + this.toasterService.error(`Fill all required fields`); + } else { + let captureSettings: CapturingSettings = { + capture_file_name: this.inputForm.get('fileName').value, + data_link_type: this.inputForm.get('linkType').value + }; + + this.linkService.startCaptureOnLink(this.server, this.link, captureSettings).subscribe(() => { + this.dialogRef.close(); + }); + } + } + + onNoClick() { + this.dialogRef.close(); + } +} diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index 1a3d2c4e..11ea2a36 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -40,6 +40,8 @@ import { RecentlyOpenedProjectService } from '../../services/recentlyOpenedProje import { MapLinkToLinkConverter } from '../../cartography/converters/map/map-link-to-link-converter'; import { Link } from '../../models/link'; import { Project } from '../../models/project'; +import { CapturingSettings } from '../../models/capturingSettings'; +import { LinkWidget } from '../../cartography/widgets/link'; export class MockedProgressService { public activate() {} @@ -112,8 +114,16 @@ export class MockedDrawingService { export class MockedLinkService { constructor() {} + getLink(server: Server, projectId: string, linkId: string) { + return of({}); + } + deleteLink(_server: Server, link: Link){ - return of({}) + return of({}); + } + + updateLink(server: Server, link: Link) { + return of({}); } createLink() { @@ -123,6 +133,14 @@ export class MockedLinkService { updateNodes() { return of({}); } + + startCaptureOnLink(server: Server, link: Link, settings: CapturingSettings) { + return of({}); + } + + getAvailableFilters(server: Server, link: Link) { + return of({}); + } } export class MockedDrawingsDataSource { @@ -145,7 +163,7 @@ export class MockedNodesDataSource { clear() {} get() { - return of({}); + return {status: 'started'}; } update() { @@ -179,6 +197,7 @@ describe('ProjectMapComponent', () => { { provide: ProjectWebServiceHandler }, { provide: MapChangeDetectorRef }, { provide: NodeWidget }, + { provide: LinkWidget }, { provide: DrawingsWidget }, { provide: MapNodeToNodeConverter }, { provide: MapDrawingToDrawingConverter }, diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 05b15f80..0681ef01 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -30,7 +30,7 @@ import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-nod import { SettingsService, Settings } from '../../services/settings.service'; import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component'; import { ToolsService } from '../../services/tools.service'; -import { DrawingContextMenu } from '../../cartography/events/event-source'; +import { DrawingContextMenu, LinkContextMenu } from '../../cartography/events/event-source'; import { MapDrawingToDrawingConverter } from '../../cartography/converters/map/map-drawing-to-drawing-converter'; import { SelectionManager } from '../../cartography/managers/selection-manager'; import { SelectionTool } from '../../cartography/tools/selection-tool'; @@ -42,6 +42,7 @@ import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-l import { RecentlyOpenedProjectService } from '../../services/recentlyOpenedProject.service'; import { MapLink } from '../../cartography/models/map/map-link'; import { MapLinkToLinkConverter } from '../../cartography/converters/map/map-link-to-link-converter'; +import { LinkWidget } from '../../cartography/widgets/link'; @Component({ @@ -95,6 +96,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private mapChangeDetectorRef: MapChangeDetectorRef, private nodeWidget: NodeWidget, private drawingsWidget: DrawingsWidget, + private linkWidget: LinkWidget, private mapNodeToNode: MapNodeToNodeConverter, private mapDrawingToDrawing: MapDrawingToDrawingConverter, private mapLabelToLabel: MapLabelToLabelConverter, @@ -219,6 +221,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.toolsService.selectionToolActivation(true); } + const onLinkContextMenu = this.linkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => { + const link = this.mapLinkToLink.convert(eventLink.link); + this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX); + }); + const onNodeContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => { const node = this.mapNodeToNode.convert(eventNode.node); this.contextMenu.openMenuForNode(node, eventNode.event.pageY, eventNode.event.pageX); @@ -253,6 +260,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.contextMenu.openMenuForListOfElements(drawings, nodes, labels, links, event.pageY, event.pageX); }); + this.subscriptions.push(onLinkContextMenu); this.subscriptions.push(onNodeContextMenu); this.subscriptions.push(onDrawingContextMenu); this.subscriptions.push(onContextMenu); diff --git a/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts b/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts index 78a806b8..a346e751 100644 --- a/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts +++ b/src/app/components/snapshots/snapshot-menu-item/snapshot-menu-item.component.ts @@ -48,11 +48,6 @@ export class SnapshotMenuItemComponent implements OnInit { (created_snapshot: Snapshot) => { this.toaster.success(`Snapshot '${snapshot.name}' has been created.`); progress.close(); - }, - response => { - const error = response.json(); - this.toaster.error(`Cannot create snapshot: ${error.message}`); - progress.close(); } ); diff --git a/src/app/material.imports.ts b/src/app/material.imports.ts index 9dc2260b..fdc33412 100644 --- a/src/app/material.imports.ts +++ b/src/app/material.imports.ts @@ -19,7 +19,8 @@ import { MatTooltipModule, MatStepperModule, MatRadioModule, - MatGridListModule + MatGridListModule, + MatTabsModule } from '@angular/material'; export const MATERIAL_IMPORTS = [ @@ -43,5 +44,6 @@ export const MATERIAL_IMPORTS = [ MatTooltipModule, MatStepperModule, MatRadioModule, - MatGridListModule + MatGridListModule, + MatTabsModule ]; diff --git a/src/app/models/capturingSettings.ts b/src/app/models/capturingSettings.ts new file mode 100644 index 00000000..7cf85ba8 --- /dev/null +++ b/src/app/models/capturingSettings.ts @@ -0,0 +1,4 @@ +export class CapturingSettings { + capture_file_name: string; + data_link_type: string; +} diff --git a/src/app/models/filter-description.ts b/src/app/models/filter-description.ts new file mode 100644 index 00000000..77238e27 --- /dev/null +++ b/src/app/models/filter-description.ts @@ -0,0 +1,14 @@ +export class FilterDescription { + description: string; + name: string; + parameters: Parameter[]; + type: string; +} + +interface Parameter { + maximum?: number; + minimum?: number; + name: string; + type: string; + unit?: string; +} diff --git a/src/app/models/filter.ts b/src/app/models/filter.ts new file mode 100644 index 00000000..5a27c67a --- /dev/null +++ b/src/app/models/filter.ts @@ -0,0 +1,7 @@ +export class Filter { + bpf?: string[]; + corrupt?: number[]; + delay?: number[]; + frequency_drop?: number[]; + packet_loss?: number[]; +} diff --git a/src/app/models/link.ts b/src/app/models/link.ts index d33d3845..74f06bcd 100644 --- a/src/app/models/link.ts +++ b/src/app/models/link.ts @@ -1,14 +1,17 @@ import { Node } from '../cartography/models/node'; import { LinkNode } from './link-node'; +import { Filter } from './filter'; export class Link { capture_file_name: string; capture_file_path: string; capturing: boolean; + filters?: Filter; link_id: string; link_type: string; nodes: LinkNode[]; project_id: string; + suspend: boolean; distance: number; // this is not from server length: number; // this is not from server diff --git a/src/app/models/message.ts b/src/app/models/message.ts new file mode 100644 index 00000000..3d21b1cc --- /dev/null +++ b/src/app/models/message.ts @@ -0,0 +1,4 @@ +export class Message { + name?: string; + description: string; +} diff --git a/src/app/services/link.service.ts b/src/app/services/link.service.ts index 37d2ebfa..89735923 100644 --- a/src/app/services/link.service.ts +++ b/src/app/services/link.service.ts @@ -7,16 +7,13 @@ import { HttpServer } from './http-server.service'; import { Port } from '../models/port'; import { Link } from '../models/link'; import { LinkNode } from '../models/link-node'; +import { FilterDescription } from '../models/filter-description'; +import { CapturingSettings } from '../models/capturingSettings'; @Injectable() export class LinkService { constructor(private httpServer: HttpServer) {} - deleteLink(server: Server, link: Link) { - //return this.httpServer.delete(server, `/compute/projects/${link.project_id}/vpcs/nodes/${link.nodes[0].node_id}/adapters/0/ports/0/nio`) - return this.httpServer.delete(server, `/projects/${link.project_id}/links/${link.link_id}`) - } - createLink(server: Server, source_node: Node, source_port: Port, target_node: Node, target_port: Port) { return this.httpServer.post(server, `/projects/${source_node.project_id}/links`, { nodes: [ @@ -34,6 +31,22 @@ export class LinkService { }); } + getLink(server: Server, projectId: string, linkId: string) { + return this.httpServer.get(server, `/projects/${projectId}/links/${linkId}`); + } + + deleteLink(server: Server, link: Link) { + return this.httpServer.delete(server, `/projects/${link.project_id}/links/${link.link_id}`) + } + + updateLink(server: Server, link: Link) { + return this.httpServer.put(server, `/projects/${link.project_id}/links/${link.link_id}`, link); + } + + getAvailableFilters(server: Server, link: Link) { + return this.httpServer.get(server, `/projects/${link.project_id}/links/${link.link_id}/available_filters`); + } + updateNodes(server: Server, link: Link, nodes: LinkNode[]) { const requestNodes = nodes.map(linkNode => { return { @@ -52,4 +65,16 @@ export class LinkService { return this.httpServer.put(server, `/projects/${link.project_id}/links/${link.link_id}`, { nodes: requestNodes }); } + + startCaptureOnLink(server: Server, link: Link, settings: CapturingSettings) { + return this.httpServer.post(server, `/projects/${link.project_id}/links/${link.link_id}/start_capture`, settings); + } + + stopCaptureOnLink(server: Server, link: Link) { + return this.httpServer.post(server, `/projects/${link.project_id}/links/${link.link_id}/stop_capture`, {}); + } + + streamPcap(server: Server, link: Link) { + return this.httpServer.get(server, `/projects/${link.project_id}/links/${link.link_id}/pcap`) + } } diff --git a/src/assets/resources/images/filter-capture.svg b/src/assets/resources/images/filter-capture.svg new file mode 100644 index 00000000..aa95a12c --- /dev/null +++ b/src/assets/resources/images/filter-capture.svg @@ -0,0 +1,428 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/resources/images/filter.svg b/src/assets/resources/images/filter.svg new file mode 100644 index 00000000..a67a6f9c --- /dev/null +++ b/src/assets/resources/images/filter.svg @@ -0,0 +1,708 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/resources/images/inspect.svg b/src/assets/resources/images/inspect.svg new file mode 100644 index 00000000..f2573ffc --- /dev/null +++ b/src/assets/resources/images/inspect.svg @@ -0,0 +1,312 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + + + + + + + + + + + + + + + + + + + + + + + +