metron-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jsir...@apache.org
Subject [1/3] metron git commit: METRON-1224 Add time range selection to search control (iraghumitra via james-sirota) closes apache/metron#796
Date Thu, 26 Oct 2017 08:05:06 GMT
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;
 }
-
-


Mime
View raw message