METRON-1676 PCAP UI - Add data range selector to the filter bar (tiborm via merrimanr) closes apache/metron#1119
Project: http://git-wip-us.apache.org/repos/asf/metron/repo Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/09a7f189 Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/09a7f189 Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/09a7f189 Branch: refs/heads/master Commit: 09a7f18994dc8254ca158139c44258cb7f1107ec Parents: b445bfe Author: tiborm <tibor.mel...@gmail.com> Authored: Mon Aug 6 09:14:54 2018 -0500 Committer: rmerriman <merrim...@gmail.com> Committed: Mon Aug 6 09:14:54 2018 -0500 ---------------------------------------------------------------------- .../pcap-filters/pcap-filters.component.html | 30 ++-- .../pcap-filters/pcap-filters.component.scss | 2 +- .../pcap-filters/pcap-filters.component.spec.ts | 156 ++++++++++++++++++- .../pcap/pcap-filters/pcap-filters.component.ts | 19 ++- .../metron-alerts/src/app/pcap/pcap.module.ts | 6 +- .../src/app/pcap/service/pcap.service.ts | 4 +- .../shared/directives/alert-search.directive.ts | 2 +- 7 files changed, 195 insertions(+), 24 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html index abc4cf0..f4133df 100644 --- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html +++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.html @@ -12,23 +12,33 @@ the specific language governing permissions and limitations under the License. --> <form (ngSubmit)="onSubmit()" #f="ngForm" class="form-inline pcap-search"> + + <div class="form-group"> + <label>From</label> + <app-date-picker id="startTime" [(date)]="startTimeStr"> </app-date-picker> + </div> + <div class="form-group"> + <label>To</label> + <app-date-picker id="endTime" [(date)]="endTimeStr"> </app-date-picker> + </div> + <div class="form-group"> - <label for="ip_src_addr">IP Source Address</label> - <input name="ip_src_addr" #ip_src_addr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipSrcAddr"> + <label for="ipSrcAddr">IP Source Address</label> + <input name="ipSrcAddr" #ipSrcAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipSrcAddr"> </div> <div class="form-group"> - <label for="ip_src_port">IP Source Port</label> - <input name="ip_src_port" #ip_src_port="ngModel" class="form-control" type="number" [(ngModel)]="model.ipSrcPort"> + <label for="ipSrcPort">IP Source Port</label> + <input name="ipSrcPort" #ipSrcPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipSrcPort"> </div> - <div class="form-group"><label for="ip_dest_addr">IP Dest Address</label> - <input name="ip_dest_addr" #ip_dest_addr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipDstAddr"> + <div class="form-group"><label for="ipDstAddr">IP Dest Address</label> + <input name="ipDstAddr" #ipDstAddr="ngModel" class="form-control" pattern="^(?:\d{1,3}\.){3}\d{1,3}(.\d{1,2})?$" [(ngModel)]="model.ipDstAddr"> </div> <div class="form-group"> - <label for="ip_dest_port">IP Dest Port</label> - <input id="ip_dest_port" name="ip_dest_port" #ip_dest_port="ngModel" class="form-control" type="number" [(ngModel)]="model.ipDstPort"> + <label for="ipDstPort">IP Dest Port</label> + <input id="ipDstPort" name="ipDstPort" #ipDstPort="ngModel" class="form-control" type="number" [(ngModel)]="model.ipDstPort"> </div> <div class="form-group"> @@ -37,8 +47,8 @@ </div> <div class="form-group"> - <label for="includeReverseTraffic">Include Reverse Traffic</label> - <input id="includeReverseTraffic" name="includeReverseTraffic" #includeReverseTraffic="ngModel" class="form-control" type="checkbox" [(ngModel)]="model.includeReverse"> + <label for="includeReverse">Include Reverse Traffic</label> + <input id="includeReverse" name="includeReverse" #includeReverse="ngModel" class="form-control" type="checkbox" [(ngModel)]="model.includeReverse"> </div> <div class="form-group"> http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss index 80bac08..b33e804 100644 --- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss +++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.scss @@ -28,7 +28,7 @@ } .form-group { - padding-right: 0.75em; + padding: 0 0.75em 0.75em 0; label { margin-right: 1em; } http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts index 8134f1d..c0f9c3b 100644 --- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts +++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.spec.ts @@ -1,3 +1,4 @@ + /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -15,10 +16,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { PcapFiltersComponent } from './pcap-filters.component'; import { FormsModule } from '../../../../node_modules/@angular/forms'; +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { PcapRequest } from '../model/pcap.request'; +import { emit } from 'cluster'; + +@Component({ + selector: 'app-date-picker', + template: '<input type="text" [(value)]="date">', +}) +class FakeDatePicker { + @Input() date: string; + @Output() dateChange = new EventEmitter<string>(); +} describe('PcapFiltersComponent', () => { let component: PcapFiltersComponent; @@ -29,18 +43,148 @@ describe('PcapFiltersComponent', () => { imports: [ FormsModule ], - declarations: [ PcapFiltersComponent ] + declarations: [ + FakeDatePicker, + PcapFiltersComponent, + ] }) .compileComponents(); - })); - beforeEach(() => { fixture = TestBed.createComponent(PcapFiltersComponent); component = fixture.componentInstance; fixture.detectChanges(); - }); + })); - it('should create', () => { + it('should be created', () => { expect(component).toBeTruthy(); }); + + it('From date should be bound to the component', () => { + let input = fixture.debugElement.query(By.css('#startTime')); + const dateString = '2020-11-11 11:11:11'; + input.componentInstance.dateChange.emit(dateString); + fixture.detectChanges(); + + expect(component.startTimeStr).toBe(dateString); + }); + + it('To date should be bound to the component', () => { + let input = fixture.debugElement.query(By.css('#endTime')); + const dateString = '2030-11-11 11:11:11'; + input.componentInstance.dateChange.emit(dateString); + fixture.detectChanges(); + + expect(component.endTimeStr).toBe(dateString); + }); + + it('IP Source Address should be bound to the model', () => { + let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcAddr"]'); + input.value = '192.168.0.1'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + expect(component.model.ipSrcAddr).toBe('192.168.0.1'); + }); + + it('IP Source Port should be bound to the model', () => { + let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipSrcPort"]'); + input.value = '9345'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + expect(component.model.ipSrcPort).toBe(9345); + }); + + it('IP Dest Address should be bound to the model', () => { + let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstAddr"]'); + input.value = '256.0.0.7'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + expect(component.model.ipDstAddr).toBe('256.0.0.7'); + }); + + it('IP Dest Port should be bound to the model', () => { + let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="ipDstPort"]'); + input.value = '8989'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + expect(component.model.ipDstPort).toBe(8989); + }); + + it('Protocol should be bound to the model', () => { + let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="protocol"]'); + input.value = 'TCP'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + expect(component.model.protocol).toBe('TCP'); + }); + + it('Include Reverse Traffic should be bound to the model', () => { + let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="includeReverse"]'); + input.click(); + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + expect(component.model.includeReverse).toBe(true); + }); + + it('Text filter should be bound to the model', () => { + let input: HTMLInputElement = fixture.nativeElement.querySelector('[name="protocol"]'); + input.value = 'TestStringFilter'; + input.dispatchEvent(new Event('input')); + fixture.detectChanges(); + + expect(component.model.protocol).toBe('TestStringFilter'); + }); + + it('From date should be converted to timestamp on submit', () => { + component.startTimeStr = '2220-12-12 12:12:12'; + component.search.emit = (model: PcapRequest) => { + expect(model.startTimeMs).toBe(new Date(component.startTimeStr).getTime()); + } + component.onSubmit(); + }); + + it('To date should be converted to timestamp on submit', () => { + component.endTimeStr = '2320-03-13 13:13:13'; + component.search.emit = (model: PcapRequest) => { + expect(model.endTimeMs).toBe(new Date(component.endTimeStr).getTime()); + } + component.onSubmit(); + }); + + it('Filter should have an output called search', () => { + component.search.subscribe((filterModel) => { + expect(filterModel).toBeDefined(); + }); + component.onSubmit(); + }); + + it('Filter should emit search event on submit', () => { + spyOn(component.search, 'emit'); + component.onSubmit(); + expect(component.search.emit).toHaveBeenCalled(); + }); + + it('Search event should contains the filter model', () => { + spyOn(component.search, 'emit'); + component.onSubmit(); + expect(component.search.emit).toHaveBeenCalledWith(component.model); + }); + + it('Filter model structure aka PcapRequest', () => { + expect(fixture.componentInstance.model.hasOwnProperty('startTimeMs')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('endTimeMs')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('ipSrcAddr')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('ipSrcPort')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('ipDstAddr')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('ipDstPort')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('protocol')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('packetFilter')).toBeTruthy(); + expect(fixture.componentInstance.model.hasOwnProperty('includeReverse')).toBeTruthy(); + }); + }); http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts index e16d71b..4f6a3dd 100644 --- a/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts +++ b/metron-interface/metron-alerts/src/app/pcap/pcap-filters/pcap-filters.component.ts @@ -15,8 +15,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { PcapRequest } from '../model/pcap.request' +import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core'; +import * as moment from 'moment/moment'; +import { DEFAULT_TIMESTAMP_FORMAT } from '../../utils/constants'; + +import { PcapRequest } from '../model/pcap.request'; @Component({ selector: 'app-pcap-filters', @@ -28,14 +31,24 @@ export class PcapFiltersComponent implements OnInit { @Input() queryRunning: boolean = true; @Output() search: EventEmitter<PcapRequest> = new EventEmitter<PcapRequest>(); + startTimeStr: string; + endTimeStr: string; + model = new PcapRequest(); constructor() { } ngOnInit() { + const endTime = new Date(); + const startTime = new Date().setDate(endTime.getDate() - 5); + + this.startTimeStr = moment(startTime).format(DEFAULT_TIMESTAMP_FORMAT); + this.endTimeStr = moment(endTime).format(DEFAULT_TIMESTAMP_FORMAT); } onSubmit() { - this.search.emit(this.model) + this.model.startTimeMs = new Date(this.startTimeStr).getTime(); + this.model.endTimeMs = new Date(this.endTimeStr).getTime(); + this.search.emit(this.model); } } http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts index c66b965..ef5c6c0 100644 --- a/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts +++ b/metron-interface/metron-alerts/src/app/pcap/pcap.module.ts @@ -22,6 +22,8 @@ import { HttpModule } from '@angular/http'; import { routing } from './pcap.routing'; +import { DatePickerModule } from '../shared/date-picker/date-picker.module'; + import { PcapListComponent } from './pcap-list/pcap-list.component'; import { PcapPacketComponent } from './pcap-packet/pcap-packet.component'; import { PcapFiltersComponent } from './pcap-filters/pcap-filters.component'; @@ -29,13 +31,15 @@ import { PcapPanelComponent } from './pcap-panel/pcap-panel.component'; import { PcapPacketLineComponent } from './pcap-packet-line/pcap-packet-line.component'; import { PcapService } from './service/pcap.service' + @NgModule({ imports: [ routing, CommonModule, FormsModule, - HttpModule + HttpModule, + DatePickerModule, ], declarations: [ PcapListComponent, http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts index ba209c4..6fd3e62 100644 --- a/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts +++ b/metron-interface/metron-alerts/src/app/pcap/service/pcap.service.ts @@ -67,7 +67,7 @@ export class PcapService { .onErrorResumeNext(); } - public getDownloadUrl(id: string, pageNo: number) { - return `/api/v1/pcap/${id}/raw?page=${pageNo}`; + public getDownloadUrl(id: string, pageId: number) { + return `/api/v1/pcap/${id}/raw?page=${pageId}`; } } http://git-wip-us.apache.org/repos/asf/metron/blob/09a7f189/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts b/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts index c512233..eb69b56 100644 --- a/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts +++ b/metron-interface/metron-alerts/src/app/shared/directives/alert-search.directive.ts @@ -130,7 +130,7 @@ export class AlertSearchDirective implements AfterViewInit, OnChanges { private handleMouseEvent (callback: Function) { clearTimeout(this.mouseEventTimer); - this.mouseEventTimer = setTimeout(() => { callback(); }, 100); + this.mouseEventTimer = window.setTimeout(() => { callback(); }, 100); } private mouseover($event) {