Repository: metron Updated Branches: refs/heads/master 128d4e7a8 -> 5243366c4
http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/model/date-filter-value.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/model/date-filter-value.ts b/metron-interface/metron-alerts/src/app/model/date-filter-value.ts new file mode 100644 index 0000000..1318ce2 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/model/date-filter-value.ts @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class DateFilterValue { + fromDate: number; + toDate: number; + + + constructor(fromDate = 0, toDate = 0) { + this.fromDate = fromDate; + this.toDate = toDate; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/model/filter.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/model/filter.ts b/metron-interface/metron-alerts/src/app/model/filter.ts index 24c54d8..441add4 100644 --- a/metron-interface/metron-alerts/src/app/model/filter.ts +++ b/metron-interface/metron-alerts/src/app/model/filter.ts @@ -15,12 +15,44 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import {ElasticsearchUtils} from '../utils/elasticsearch-utils'; +import {TIMESTAMP_FIELD_NAME} from '../utils/constants'; +import {Utils} from '../utils/utils'; +import {DateFilterValue} from './date-filter-value'; + export class Filter { field: string; value: string; + display: boolean; + dateFilterValue: DateFilterValue; + + static fromJSON(objs: Filter[]): Filter[] { + let filters = []; + if (objs) { + for (let obj of objs) { + filters.push(new Filter(obj.field, obj.value, obj.display)); + } + } + return filters; + } - constructor(field: string, value: string) { + constructor(field: string, value: string, display = true) { this.field = field; this.value = value; + this.display = display; + } + + getQueryString(): string { + if (this.field === TIMESTAMP_FIELD_NAME && !this.display) { + this.dateFilterValue = Utils.timeRangeToDateObj(this.value); + if (this.dateFilterValue !== null && this.dateFilterValue.toDate !== null) { + return ElasticsearchUtils.escapeESField(this.field) + ':' + + '(>=' + this.dateFilterValue.fromDate + ' AND ' + ' <=' + this.dateFilterValue.toDate + ')'; + } else { + return ElasticsearchUtils.escapeESField(this.field) + ':' + this.value; + } + } + + return ElasticsearchUtils.escapeESField(this.field) + ':' + ElasticsearchUtils.escapeESValue(this.value); } } http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/model/save-search.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/model/save-search.ts b/metron-interface/metron-alerts/src/app/model/save-search.ts index b2ee765..173f60e 100644 --- a/metron-interface/metron-alerts/src/app/model/save-search.ts +++ b/metron-interface/metron-alerts/src/app/model/save-search.ts @@ -19,18 +19,21 @@ import {QueryBuilder} from '../alerts/alerts-list/query-builder'; import {ColumnMetadata} from './column-metadata'; import {SearchRequest} from './search-request'; +import {Filter} from './filter'; export class SaveSearch { name = ''; lastAccessed = 0; searchRequest: SearchRequest; tableColumns: ColumnMetadata[]; + filters: Filter[]; public static fromJSON(obj: SaveSearch): SaveSearch { let saveSearch = new SaveSearch(); saveSearch.name = obj.name; saveSearch.lastAccessed = obj.lastAccessed; saveSearch.searchRequest = obj.searchRequest; + saveSearch.filters = Filter.fromJSON(obj.filters); saveSearch.tableColumns = ColumnMetadata.fromJSON(obj.tableColumns); return saveSearch; @@ -43,6 +46,6 @@ export class SaveSearch { let queryBuilder = new QueryBuilder(); queryBuilder.searchRequest = this.searchRequest; - return queryBuilder.generateSelectForDisplay(); + return queryBuilder.generateNameForSearchRequest(); } } http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/service/search.service.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/service/search.service.ts b/metron-interface/metron-alerts/src/app/service/search.service.ts index 71ed516..4bbcc2d 100644 --- a/metron-interface/metron-alerts/src/app/service/search.service.ts +++ b/metron-interface/metron-alerts/src/app/service/search.service.ts @@ -30,6 +30,7 @@ import {GroupRequest} from '../model/group-request'; import {GroupResult} from '../model/group-result'; import {INDEXES} from '../utils/constants'; import {ColumnMetadata} from '../model/column-metadata'; +import {QueryBuilder} from '../alerts/alerts-list/query-builder'; @Injectable() export class SearchService { @@ -83,11 +84,11 @@ export class SearchService { .catch(HttpUtil.handleError); } - public pollSearch(searchRequest: SearchRequest): Observable<SearchResponse> { + public pollSearch(queryBuilder: QueryBuilder): Observable<SearchResponse> { return this.ngZone.runOutsideAngular(() => { return this.ngZone.run(() => { return Observable.interval(this.interval * 1000).switchMap(() => { - return this.search(searchRequest); + return this.search(queryBuilder.searchRequest); }); }); }); http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.html b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.html new file mode 100644 index 0000000..475d7fc --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.html @@ -0,0 +1,17 @@ +<!-- + Licensed to the Apache Software + Foundation (ASF) under one or more contributor license agreements. See the + NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to You under the Apache License, + Version 2.0 (the "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + OR CONDITIONS OF ANY KIND, either express or implied. See the License for + the specific language governing permissions and limitations under the License. + --> +<div #inputText class="input-group"> + <input class="form-control" [(ngModel)]="dateStr" (click)="toggleDatePicker($event)"> + <span class="input-group-addon calendar"></span> +</div> http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss new file mode 100644 index 0000000..813b6a5 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.scss @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../../variables"; + +.calendar { + height: 35px; + background: #333333; + border: solid 1px #4D4D4D; + color: #999999; + + &::after { + font-family: "FontAwesome"; + content: '\f073'; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.spec.ts b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.spec.ts new file mode 100644 index 0000000..994ac02 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DatePickerComponent } from './date-picker.component'; + +describe('DatePickerComponent', () => { + let component: DatePickerComponent; + let fixture: ComponentFixture<DatePickerComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DatePickerComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DatePickerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts new file mode 100644 index 0000000..3ed7df9 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.component.ts @@ -0,0 +1,77 @@ +import { Component, OnInit, ViewChild, ElementRef, OnChanges, SimpleChanges, Input, Output, EventEmitter } from '@angular/core'; +import * as moment from 'moment/moment'; +import * as Pikaday from "pikaday-time"; + +@Component({ + selector: 'app-date-picker', + templateUrl: './date-picker.component.html', + styleUrls: ['./date-picker.component.scss'] +}) +export class DatePickerComponent implements OnInit, OnChanges { + defaultDateStr = 'now'; + picker: Pikaday; + dateStr = this.defaultDateStr; + + @Input() date = ''; + @Input() minDate = ''; + @Output() dateChange = new EventEmitter<string>(); + @ViewChild('inputText') inputText: ElementRef; + + constructor(private elementRef: ElementRef) {} + + ngOnInit() { + let _datePickerComponent = this; + let pikadayConfig = { + field: this.elementRef.nativeElement, + showSeconds: true, + use24hour: true, + onSelect: function() { + _datePickerComponent.dateStr = this.getMoment().format('YYYY-MM-DD HH:mm:ss'); + setTimeout(() => _datePickerComponent.dateChange.emit(_datePickerComponent.dateStr), 0); + } + }; + this.picker = new Pikaday(pikadayConfig); + this.setDate(); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes && changes['minDate'] && this.picker) { + this.setMinDate(); + } + + if (changes && changes['date'] && this.picker) { + this.setDate(); + } + } + + setDate() { + if (this.date === '') { + this.dateStr = this.defaultDateStr; + } else { + this.dateStr = this.date; + this.picker.setDate(this.dateStr); + } + } + + setMinDate() { + let currentDate = new Date(this.dateStr).getTime(); + let currentMinDate = new Date(this.minDate).getTime(); + if (currentMinDate > currentDate) { + this.dateStr = this.defaultDateStr; + } + this.picker.setMinDate(new Date(this.minDate)); + this.picker.setDate(moment(this.minDate).endOf('day').format('YYYY-MM-DD HH:mm:ss')); + } + + toggleDatePicker($event) { + if (this.picker) { + if (this.picker.isVisible()) { + this.picker.hide(); + } else { + this.picker.show(); + } + + $event.stopPropagation(); + } + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts new file mode 100644 index 0000000..ded9881 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/date-picker/date-picker.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import {DatePickerComponent} from './date-picker.component'; +import {SharedModule} from '../shared.module'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule + ], + declarations: [DatePickerComponent], + exports: [DatePickerComponent] +}) +export class DatePickerModule { } http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.spec.ts b/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.spec.ts new file mode 100644 index 0000000..17cfef7 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.spec.ts @@ -0,0 +1,8 @@ +import { MapKeysPipe } from './map-keys.pipe'; + +describe('MapKeysPipe', () => { + it('create an instance', () => { + const pipe = new MapKeysPipe(); + expect(pipe).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.ts b/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.ts new file mode 100644 index 0000000..5bf8013 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/pipes/map-keys.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ + name: 'mapKeys' +}) +export class MapKeysPipe implements PipeTransform { + + transform(value: any, args?: any): any { + return value ? Object.keys(value) : []; + } + +} http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/shared.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/shared.module.ts b/metron-interface/metron-alerts/src/app/shared/shared.module.ts index e26ec9b..41290a4 100644 --- a/metron-interface/metron-alerts/src/app/shared/shared.module.ts +++ b/metron-interface/metron-alerts/src/app/shared/shared.module.ts @@ -24,6 +24,7 @@ import { NavContentDirective } from './directives/nav-content.directive'; import { CenterEllipsesPipe } from './pipes/center-ellipses.pipe'; import { AlertSearchDirective } from './directives/alert-search.directive'; import { ColumnNameTranslatePipe } from './pipes/column-name-translate.pipe'; +import { MapKeysPipe } from './pipes/map-keys.pipe'; import { AlertSeverityHexagonDirective } from './directives/alert-severity-hexagon.directive'; @NgModule({ @@ -37,6 +38,7 @@ import { AlertSeverityHexagonDirective } from './directives/alert-severity-hexag CenterEllipsesPipe, AlertSearchDirective, ColumnNameTranslatePipe, + MapKeysPipe, AlertSeverityHexagonDirective ], exports: [ @@ -48,6 +50,7 @@ import { AlertSeverityHexagonDirective } from './directives/alert-severity-hexag CenterEllipsesPipe, AlertSearchDirective, ColumnNameTranslatePipe, + MapKeysPipe, AlertSeverityHexagonDirective ] }) http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.html b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.html new file mode 100644 index 0000000..b65528d --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.html @@ -0,0 +1,57 @@ +<!-- + Licensed to the Apache Software + Foundation (ASF) under one or more contributor license agreements. See the + NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to You under the Apache License, + Version 2.0 (the "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + OR CONDITIONS OF ANY KIND, either express or implied. See the License for + the specific language governing permissions and limitations under the License. + --> +<button class="btn btn-secondary btn-search" [disabled]="disabled" type="button" data-animation="false" data-toggle="collapse" data-target="#time-range" aria-expanded="false" aria-controls="time-range"> + <span> {{selectedTimeRangeValue}} </span> + <br/> <span style="font-size: 8px;" *ngIf="selectedTimeRangeValue !== 'All time'"> {{fromDateStr}} to {{toDateStr}}</span> +</button> + +<div #datePicker class="collapse" id="time-range"> + <div class="card card-block"> + <div class="container-fluid no-gutters h-100"> + <div class="row h-100"> + <div class="col-4 time-range"> + <div class="title">Time Range</div> <br> + <form> + <div class="form-group"> + <label>FROM</label> + <app-date-picker [(date)]="datePickerFromDate"> </app-date-picker> + </div> + <div class="form-group"> + <label>TO</label> + <app-date-picker [(date)]="datePickerToDate"> </app-date-picker> + </div> + <button class="btn btn-primary pull-right" type="button" [disabled]='datePickerFromDate===""' (click)="applyCustomDate()">APPLY</button> + </form> + </div> + <div class="col-8 quick-ranges pr-0"> + <div class="title"> Quick Ranges </div> <br> + <div class="row no-gutters"> + <div class="col-3"> + <span *ngFor="let key of timeRangeMappingCol1 | mapKeys" (click)="selectTimeRange($event, timeRangeMappingCol1[key])"> {{ key }} </span> <br> + </div> + <div class="col-3"> + <span *ngFor="let key of timeRangeMappingCol2 | mapKeys" (click)="selectTimeRange($event, timeRangeMappingCol2[key])"> {{ key }} </span> <br> + </div> + <div class="col-3"> + <span *ngFor="let key of timeRangeMappingCol3 | mapKeys" (click)="selectTimeRange($event, timeRangeMappingCol3[key])"> {{ key }} </span> <br> + </div> + <div class="col-3"> + <span *ngFor="let key of timeRangeMappingCol4 | mapKeys" (click)="selectTimeRange($event, timeRangeMappingCol4[key])"> {{ key }} </span> <br> + </div> + </div> + </div> + </div> + </div> + </div> +</div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.scss ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.scss b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.scss new file mode 100644 index 0000000..7f5faf0 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.scss @@ -0,0 +1,106 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "../../../variables"; + + +:host { + height: 100%; +} + +.btn-search { + height: 100%; + color: $silver; + cursor: pointer; + line-height: 1; + padding: 2px 20px; + border-radius: 0px; + font-family: Roboto; + background: $mine-shaft-11; + border: 1px solid $tundora !important; + + &:focus { + box-shadow: none; + } + + &::after { + font-family: "FontAwesome"; + content: '\f0d7'; + padding-left: 5px; + color: $dusty-grey; + position: absolute; + top: 15px; + right: 5px; + } +} + +.collapse, .collapsing { + position: absolute; + margin-top: 5px; + width: 930px; + height: 257px; + z-index: 99; + right: 0; + + .card, .card-block { + height: inherit; + background: $mine-shaft-1; + border: 1px solid $mine-shaft-8; + } +} + +.title { + font-size: 20px; +} + +.time-range { + border-right: 1px solid $abbey; +} + +.input-group { + position: relative; + width: 100%; + + .form-control { + display: block; + flex-direction: initial; + justify-content: initial; + } +} + +.quick-ranges { + span { + color: #1E87AF; + font-size: 14px; + line-height: 1.7; + cursor: pointer; + width: 100%; + display: block; + padding: 0px 5px; + font-family: Roboto; + + &:hover { + background: #1F91BE; + color: #FDFEFE; + } + } +} + +form { + margin-top: 5px; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.spec.ts b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.spec.ts new file mode 100644 index 0000000..1e35979 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TimeRangeComponent } from './time-range.component'; + +describe('TimeRangeComponent', () => { + let component: TimeRangeComponent; + let fixture: ComponentFixture<TimeRangeComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ TimeRangeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TimeRangeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.ts b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.ts new file mode 100644 index 0000000..89f57a1 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.component.ts @@ -0,0 +1,192 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Component, OnInit, ViewChild, ElementRef, HostListener, EventEmitter, Output, Input, OnChanges, SimpleChanges} from '@angular/core'; +import * as moment from 'moment/moment'; + +import {Filter} from '../../model/filter'; +import { + DEFAULT_TIMESTAMP_FORMAT, CUSTOMM_DATE_RANGE_LABEL, + TIMESTAMP_FIELD_NAME, ALL_TIME +} from '../../utils/constants'; +import {DateFilterValue} from '../../model/date-filter-value'; + +@Component({ + selector: 'app-time-range', + templateUrl: './time-range.component.html', + styleUrls: ['./time-range.component.scss'] +}) +export class TimeRangeComponent implements OnInit, OnChanges { + toDateStr = ''; + fromDateStr = ''; + datePickerFromDate = ''; + datePickerToDate = ''; + selectedTimeRangeValue = 'All time'; + + @Input() disabled = false; + @Input() selectedTimeRange: Filter; + @ViewChild('datePicker') datePicker: ElementRef; + @Output() timeRangeChange = new EventEmitter<Filter>(); + + timeRangeMappingCol1 = { + 'Last 7 days': 'last-7-days', + 'Last 30 days': 'last-30-days', + 'Last 60 days': 'last-60-days', + 'Last 90 days': 'last-90-days', + 'Last 6 months': 'last-6-months', + 'Last 1 year': 'last-1-year', + 'Last 2 years': 'last-2-years', + 'Last 5 years': 'last-5-years' + }; + timeRangeMappingCol2 = { + 'Yesterday': 'yesterday', + 'Day before yesterday': 'day-before-yesterday', + 'This day last week': 'this-day-last-week', + 'Previous week': 'previous-week', + 'Previous month': 'previous-month', + 'Previous year': 'previous-year', + 'All time': ALL_TIME + }; + timeRangeMappingCol3 = { + 'Today': 'today', + 'Today so far': 'today-so-far', + 'This week': 'this-week', + 'This week so far': 'this-week-so-far', + 'This month': 'this-month', + 'This year': 'this-year' + }; + timeRangeMappingCol4 = { + 'Last 5 minutes': 'last-5-minutes', + 'Last 15 minutes': 'last-15-minutes', + 'Last 30 minutes': 'last-30-minutes', + 'Last 1 hour': 'last-1-hour', + 'Last 3 hours': 'last-3-hours', + 'Last 6 hours': 'last-6-hours', + 'Last 12 hours': 'last-12-hours', + 'Last 24 hours': 'last-24-hours' + }; + + constructor() { } + + ngOnChanges(changes: SimpleChanges) { + if (changes && changes['selectedTimeRange']) { + this.onSelectedTimeRangeChange(); + } + } + + ngOnInit() { + } + + onSelectedTimeRangeChange() { + let foundQuickRange = false; + let merged = Object.assign({}, this.timeRangeMappingCol1, this.timeRangeMappingCol2, this.timeRangeMappingCol3, this.timeRangeMappingCol4); + Object.keys(merged).forEach(key => { + if (this.selectedTimeRange.value === merged[key]) { + foundQuickRange = true; + this.selectedTimeRangeValue = key; + if (this.selectedTimeRange.dateFilterValue) { + this.toDateStr = moment(this.selectedTimeRange.dateFilterValue.toDate).format(DEFAULT_TIMESTAMP_FORMAT); + this.fromDateStr = moment(this.selectedTimeRange.dateFilterValue.fromDate).format(DEFAULT_TIMESTAMP_FORMAT); + + this.datePickerFromDate = ''; + this.datePickerToDate = ''; + } + } + }); + + if (!foundQuickRange) { + this.selectedTimeRangeValue = CUSTOMM_DATE_RANGE_LABEL; + this.toDateStr = this.selectedTimeRange.dateFilterValue.toDate !== null ? + moment(this.selectedTimeRange.dateFilterValue.toDate).format(DEFAULT_TIMESTAMP_FORMAT) : + 'now'; + this.fromDateStr = moment(this.selectedTimeRange.dateFilterValue.fromDate).format(DEFAULT_TIMESTAMP_FORMAT); + + this.datePickerFromDate = this.fromDateStr; + this.datePickerToDate = this.selectedTimeRange.dateFilterValue.toDate !== null ? this.toDateStr : ''; + } + } + + getTimeRangeStr() { + let mappingVal = this.timeRangeMappingCol1[this.selectedTimeRangeValue]; + if (!mappingVal) { + mappingVal = this.timeRangeMappingCol2[this.selectedTimeRangeValue]; + } + if (!mappingVal) { + mappingVal = this.timeRangeMappingCol3[this.selectedTimeRangeValue]; + } + if (!mappingVal) { + mappingVal = this.timeRangeMappingCol4[this.selectedTimeRangeValue]; + } + return mappingVal; + } + + selectTimeRange($event, range: string) { + this.hideDatePicker(); + this.selectedTimeRangeValue = $event.target.textContent.trim(); + this.datePickerFromDate = ''; + this.datePickerToDate = ''; + this.timeRangeChange.emit(new Filter(TIMESTAMP_FIELD_NAME, range, false)); + } + + hideDatePicker() { + this.datePicker.nativeElement.classList.remove('show'); + } + + applyCustomDate() { + this.hideDatePicker(); + this.selectedTimeRangeValue = CUSTOMM_DATE_RANGE_LABEL; + this.toDateStr = this.datePickerToDate.length > 0 ? moment(this.datePickerToDate).format(DEFAULT_TIMESTAMP_FORMAT) : 'NOW'; + this.fromDateStr = moment(this.datePickerFromDate).format(DEFAULT_TIMESTAMP_FORMAT); + + let toDate = this.datePickerToDate.length > 0 ? new Date(this.toDateStr).getTime() : null; + let fromDate = new Date(this.fromDateStr).getTime(); + let toDateExpression = this.datePickerToDate.length > 0 ? (' AND ' + ' <=' + toDate) : ''; + + let value = '(>=' + fromDate + toDateExpression + ')'; + let filter = new Filter(TIMESTAMP_FIELD_NAME, value, false); + filter.dateFilterValue = new DateFilterValue(fromDate, toDate); + this.timeRangeChange.emit(filter); + } + + isPikaSelectElement(targetElement: HTMLElement): boolean { + while(targetElement) { + if (targetElement.classList.toString().startsWith('pika')){ + return true; + } + targetElement = targetElement.parentElement; + } + + return false; + } + + @HostListener('document:click', ['$event', '$event.target']) + onClick(event: MouseEvent, targetElement: HTMLElement): void { + if (!targetElement) { + return; + } + + if(this.isPikaSelectElement(targetElement)) { + return; + } + + const clickedInside = this.datePicker.nativeElement.contains(targetElement); + if (!clickedInside) { + this.hideDatePicker(); + } + } + +} http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/shared/time-range/time-range.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/shared/time-range/time-range.module.ts b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.module.ts new file mode 100644 index 0000000..412ea39 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/shared/time-range/time-range.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import {TimeRangeComponent} from './time-range.component'; +import {DatePickerModule} from '../date-picker/date-picker.module'; +import {SharedModule} from '../shared.module'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + DatePickerModule + ], + declarations: [TimeRangeComponent], + exports: [TimeRangeComponent] +}) +export class TimeRangeModule { } http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/utils/constants.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/utils/constants.ts b/metron-interface/metron-alerts/src/app/utils/constants.ts index b71f89e..156c65f 100644 --- a/metron-interface/metron-alerts/src/app/utils/constants.ts +++ b/metron-interface/metron-alerts/src/app/utils/constants.ts @@ -24,8 +24,14 @@ export const ALERTS_SAVED_SEARCH = 'metron-alerts-saved-search'; export const ALERTS_TABLE_METADATA = 'metron-alerts-table-metadata'; export const ALERTS_COLUMN_NAMES = 'metron-alerts-column-names'; +export let THREAT_SCORE_FIELD_NAME = 'threat:triage:score'; +export let TIMESTAMP_FIELD_NAME = 'timestamp'; +export let ALL_TIME = 'all-time'; + +export let DEFAULT_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH:mm:ss'; +export let CUSTOMM_DATE_RANGE_LABEL = 'Date Range'; + export let TREE_SUB_GROUP_SIZE = 5; export let DEFAULT_FACETS = ['source:type', 'ip_src_addr', 'ip_dst_addr', 'host', 'enrichments:geo:ip_dst_addr:country']; export let DEFAULT_GROUPS = ['source:type', 'ip_src_addr', 'ip_dst_addr', 'host', 'enrichments:geo:ip_dst_addr:country']; export let INDEXES = environment.indices ? environment.indices.split(',') : ['websphere', 'snort', 'asa', 'bro', 'yaf']; - http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/utils/elasticsearch-utils.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/utils/elasticsearch-utils.ts b/metron-interface/metron-alerts/src/app/utils/elasticsearch-utils.ts index bbd4112..1f5bcfc 100644 --- a/metron-interface/metron-alerts/src/app/utils/elasticsearch-utils.ts +++ b/metron-interface/metron-alerts/src/app/utils/elasticsearch-utils.ts @@ -71,4 +71,16 @@ export class ElasticsearchUtils { return message; } + + public static escapeESField(field: string) { + return field.replace(/:/g, '\\:'); + } + + public static escapeESValue(value: string) { + return String(value) + .replace(/[\*\+\-=~><\"\?^\${}\(\)\:\!\/[\]\\\s]/g, '\\$&') // replace single special characters + .replace(/\|\|/g, '\\||') // replace || + .replace(/\&\&/g, '\\&&'); // replace && + } + } http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/app/utils/utils.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/app/utils/utils.ts b/metron-interface/metron-alerts/src/app/utils/utils.ts new file mode 100644 index 0000000..57a6355 --- /dev/null +++ b/metron-interface/metron-alerts/src/app/utils/utils.ts @@ -0,0 +1,184 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as moment from 'moment/moment'; + +import {DEFAULT_TIMESTAMP_FORMAT, TIMESTAMP_FIELD_NAME} from './constants'; +import {DateFilterValue} from '../model/date-filter-value'; + +export class Utils { + + public static timeRangeToDateObj(range:string) { + let timeRangeToDisplayStr = Utils.timeRangeToDisplayStr(range); + if (timeRangeToDisplayStr != null) { + let toDate = new Date((timeRangeToDisplayStr.toDate)).getTime(); + let fromDate = new Date((timeRangeToDisplayStr.fromDate)).getTime(); + + return new DateFilterValue(fromDate, toDate); + } + let timeRangeToEpoc = Utils.parseTimeRange(range); + if (timeRangeToEpoc !== null) { + return new DateFilterValue(timeRangeToEpoc.fromDate, timeRangeToEpoc.toDate); + } + return null; + } + + public static parseTimeRange(range:string) { + let parsed = range.replace(/^\(>=/, '') + .replace(/\)$/, '') + .replace(/<=/, '').split('AND'); + if (parsed.length === 2 && !isNaN(Number(parsed[0])) && !isNaN(Number(parsed[1]))) { + return {toDate: Number(parsed[1]), fromDate: Number(parsed[0])}; + } + if (parsed.length === 1 && !isNaN(Number(parsed[0]))) { + return {toDate: null, fromDate: Number(parsed[0])}; + } + + return null; + } + + public static timeRangeToDisplayStr(range:string) { + let toDate = ''; + let fromDate = ''; + + switch (range) { + case 'last-7-days': + fromDate = moment().subtract(7, 'days').local().format(); + toDate = moment().local().format(); + break; + case 'last-30-days': + fromDate = moment().subtract(30, 'days').local().format(); + toDate = moment().local().format(); + break; + case 'last-60-days': + fromDate = moment().subtract(60, 'days').local().format(); + toDate = moment().local().format(); + break; + case 'last-90-days': + fromDate = moment().subtract(90, 'days').local().format(); + toDate = moment().local().format(); + break; + case 'last-6-months': + fromDate = moment().subtract(6, 'months').local().format(); + toDate = moment().local().format(); + break; + case 'last-1-year': + fromDate = moment().subtract(1, 'year').local().format(); + toDate = moment().local().format(); + break; + case 'last-2-years': + fromDate = moment().subtract(2, 'years').local().format(); + toDate = moment().local().format(); + break; + case 'last-5-years': + fromDate = moment().subtract(5, 'years').local().format(); + toDate = moment().local().format(); + break; + case 'all-time': + fromDate = '1970-01-01T05:30:00+05:30'; + toDate = '2100-01-01T05:30:00+05:30'; + break; + case 'yesterday': + fromDate = moment().subtract(1, 'days').startOf('day').local().format(); + toDate = moment().subtract(1, 'days').endOf('day').local().format(); + break; + case 'day-before-yesterday': + fromDate = moment().subtract(2, 'days').startOf('day').local().format(); + toDate = moment().subtract(2, 'days').endOf('day').local().format(); + break; + case 'this-day-last-week': + fromDate = moment().subtract(7, 'days').startOf('day').local().format(); + toDate = moment().subtract(7, 'days').endOf('day').local().format(); + break; + case 'previous-week': + fromDate = moment().subtract(1, 'weeks').startOf('week').local().format(); + toDate = moment().subtract(1, 'weeks').endOf('week').local().format(); + break; + case 'previous-month': + fromDate = moment().subtract(1, 'months').startOf('month').local().format(); + toDate = moment().subtract(1, 'months').endOf('month').local().format(); + break; + case 'previous-year': + fromDate = moment().subtract(1, 'years').startOf('year').local().format(); + toDate = moment().subtract(1, 'years').endOf('year').local().format(); + break; + case 'today': + fromDate = moment().startOf('day').local().format(); + toDate = moment().endOf('day').local().format(); + break; + case 'today-so-far': + fromDate = moment().startOf('day').local().format(); + toDate = moment().local().format(); + break; + case 'this-week': + fromDate = moment().startOf('week').local().format(); + toDate = moment().endOf('week').local().format(); + break; + case 'this-week-so-far': + fromDate = moment().startOf('week').local().format(); + toDate = moment().local().format(); + break; + case 'this-month': + fromDate = moment().startOf('month').local().format(); + toDate = moment().endOf('month').local().format(); + break; + case 'this-year': + fromDate = moment().startOf('year').local().format(); + toDate = moment().endOf('year').local().format(); + break; + case 'last-5-minutes': + fromDate = moment().subtract(5, 'minutes').local().format(); + toDate = moment().local().format(); + break; + case 'last-15-minutes': + fromDate = moment().subtract(15, 'minutes').local().format(); + toDate = moment().local().format(); + break; + case 'last-30-minutes': + fromDate = moment().subtract(30, 'minutes').local().format(); + toDate = moment().local().format(); + break; + case 'last-1-hour': + fromDate = moment().subtract(60, 'minutes').local().format(); + toDate = moment().local().format(); + break; + case 'last-3-hours': + fromDate = moment().subtract(3, 'hours').local().format(); + toDate = moment().local().format(); + break; + case 'last-6-hours': + fromDate = moment().subtract(6, 'hours').local().format(); + toDate = moment().local().format(); + break; + case 'last-12-hours': + fromDate = moment().subtract(12, 'hours').local().format(); + toDate = moment().local().format(); + break; + case 'last-24-hours': + fromDate = moment().subtract(24, 'hours').local().format(); + toDate = moment().local().format(); + break; + default: + return null; + } + + toDate = moment(toDate).format(DEFAULT_TIMESTAMP_FORMAT); + fromDate = moment(fromDate).format(DEFAULT_TIMESTAMP_FORMAT); + + return {toDate: toDate, fromDate: fromDate}; + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/5243366c/metron-interface/metron-alerts/src/styles.scss ---------------------------------------------------------------------- diff --git a/metron-interface/metron-alerts/src/styles.scss b/metron-interface/metron-alerts/src/styles.scss index b34fc39..0958685 100644 --- a/metron-interface/metron-alerts/src/styles.scss +++ b/metron-interface/metron-alerts/src/styles.scss @@ -20,6 +20,7 @@ @import "_variables.scss"; @import "slider.scss"; @import "metron-dialog.scss"; +@import "../node_modules/pikaday-time/scss/pikaday.scss"; @import "hexagon"; body, @@ -243,6 +244,14 @@ form } } +.pika-select { + height: 20px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0px 15px; +} + .tooltip-inner { opacity: 0.9; font-size: 11px; @@ -258,5 +267,3 @@ hr { margin: 0.3rem 0; padding: 0; } - -
