You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by sa...@apache.org on 2019/08/30 15:27:14 UTC

[metron] branch master updated: METRON-2199 [UI] Add ability to turn off query building in Alerts UI search input (sardell) closes apache/metron#1477

This is an automated email from the ASF dual-hosted git repository.

sardell pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/metron.git


The following commit(s) were added to refs/heads/master by this push:
     new 888c4bc  METRON-2199 [UI] Add ability to turn off query building in Alerts UI search input (sardell) closes apache/metron#1477
888c4bc is described below

commit 888c4bc8f353ea0424f28919ab33b5f4ad861d25
Author: sardell <sh...@gmail.com>
AuthorDate: Fri Aug 30 17:09:46 2019 +0200

    METRON-2199 [UI] Add ability to turn off query building in Alerts UI search input (sardell) closes apache/metron#1477
---
 .../alert-details/alert-details.component.spec.ts  |  4 +-
 .../alerts/alerts-list/alerts-list.component.html  | 22 ++++--
 .../alerts/alerts-list/alerts-list.component.scss  | 24 ++++++
 .../alerts-list/alerts-list.component.spec.ts      | 86 ++++++++++++++++++++--
 .../alerts/alerts-list/alerts-list.component.ts    | 72 +++++++++++++++---
 .../table-view/table-view.component.spec.ts        |  7 +-
 .../tree-view/tree-view.component.spec.ts          |  7 +-
 7 files changed, 193 insertions(+), 29 deletions(-)

diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.spec.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.spec.ts
index 63c22e3..92843e3 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.spec.ts
@@ -104,7 +104,9 @@ describe('AlertDetailsComponent', () => {
         AuthenticationService,
         AlertsService,
         UpdateService,
-        GlobalConfigService,
+        { provide: GlobalConfigService, useValue: {
+          get: () => { return of({})}
+        }},
         {
           provide: DialogService,
           useValue: {
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.html b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.html
index 721426d..e56fb1b 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.html
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.html
@@ -19,16 +19,28 @@
                     <span class="input-group-prepend">
                         <button class="btn btn-secondary btn-saved-searches" type="button" (click)="showSavedSearches()">Searches</button>
                     </span>
-                    <div appAceEditor style="width:100%;" placeholder="Search Alerts" [text]="queryBuilder.displayQuery" (textChanged)="onSearch($event)"> </div>
+                    <div appAceEditor *ngIf="!hideQueryBuilder" class="flex-fill" placeholder="Search Alerts" [text]="queryBuilder.displayQuery" (textChanged)="onSearch($event)"> </div>
+                    <div class="flex-fill" [class.d-none]="!hideQueryBuilder">
+                        <input class="manual-query-input" data-qe-id="manual-query-input" type="text" #manualQuery >
+                    </div>
+                    <span class="input-group-append">
+                        <button class="btn btn-secondary btn-options" (click)="toggleQueryBuilder()">
+                            <span *ngIf="hideQueryBuilder">Use Query Builder</span>
+                            <span *ngIf="!hideQueryBuilder">Use Manual Query</span>
+                        </button>
+                    </span>
                     <span class="input-group-append">
                         <button class="btn btn-secondary btn-search-clear" type="button" (click)="onClear()"></button>
                     </span>
-                    <span class="input-group-append" style="white-space: nowrap;">
+                    <span class="input-group-append" style="white-space: nowrap;" [class.d-none]="hideQueryBuilder">
                         <app-time-range class="d-flex position-relative" (timeRangeChange)="onTimeRangeChange($event)" [disabled]="timeStampFilterPresent" [selectedTimeRange]="selectedTimeRange"> </app-time-range>
                     </span>
-                    <span class="input-group-append">
+                    <span class="input-group-append" [class.d-none]="hideQueryBuilder">
                         <button data-qe-id="alert-search-btn" class="btn btn-secondary btn-search rounded-right" type="button" data-name="search" (click)="onSearch(alertSearchDirective.getSeacrhText())"></button>
                     </span>
+                    <span class="input-group-append" [class.d-none]="!hideQueryBuilder">
+                        <button class="btn btn-secondary btn-search rounded-right" type="button" data-name="search" (click)="search(false, null)"></button>
+                    </span>
                     <div class="input-group-append">
                         <span class="save-button" (click)="showSaveSearch()">
                         </span>
@@ -72,7 +84,7 @@
 
 <div class="container-fluid no-gutters">
     <div class="row">
-      <div class="px-0" style="width: 200px;max-width: 200px;">
+      <div class="px-0" style="width: 200px;max-width: 200px;" [class.d-none]="hideQueryBuilder">
         <app-alert-filters [facets]="searchResponse.facetCounts" (facetFilterChange)="onAddFacetFilter($event)"> </app-alert-filters>
       </div>
       <div class="col px-0 pl-4" style="overflow: auto;">
@@ -100,7 +112,7 @@
                            [alertsColumnsToDisplay]="alertsColumnsToDisplay"
                            [selectedAlerts]="selectedAlerts"
                            [globalConfig]="globalConfig"
-                           [query]="queryBuilder.generateSelect()"
+                           [query]="queryForTreeView()"
                            [groups]="groups"
                            (onResize)="onResize()"
                            (onAddFilter)="onAddFilter($event)"
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.scss b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.scss
index fe2f54c..c39887d 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.scss
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.scss
@@ -83,6 +83,19 @@ $searchbox-height: 42px;
     }
   }
 
+  .btn-options {
+    background: $mine-shaft-1;
+    border-bottom: 1px solid $tundora;
+    border-left: none;
+    border-right: none;
+    border-top: 1px solid $tundora;
+    color: $gothic;
+
+    &:focus {
+      box-shadow: none;
+    }
+  }
+
   .btn-search-clear {
     border-top: 1px solid $tundora;
     border-bottom: 1px solid $tundora;
@@ -300,4 +313,15 @@ $searchbox-height: 42px;
   .ace_hidden-cursors .ace_cursor {
     opacity: 0;
   }
+
+}
+
+.manual-query-input {
+  background: $mine-shaft-1;
+  border: 1px solid $tundora;
+  color: #F8F8F2;
+  font-size: 12px;
+  height: 100%;
+  padding-left: .5rem;
+  width: 100%;
 }
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.spec.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.spec.ts
index 7fbf9cc..8cbff8f 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.spec.ts
@@ -28,6 +28,7 @@ import { SaveSearchService } from 'app/service/save-search.service';
 import { MetaAlertService } from 'app/service/meta-alert.service';
 import { GlobalConfigService } from 'app/service/global-config.service';
 import { DialogService } from 'app/service/dialog.service';
+import { SearchRequest } from 'app/model/search-request';
 import { Observable, of, Subject } from 'rxjs';
 import { Filter } from 'app/model/filter';
 import { QueryBuilder } from './query-builder';
@@ -39,6 +40,26 @@ describe('AlertsListComponent', () => {
 
   let component: AlertsListComponent;
   let fixture: ComponentFixture<AlertsListComponent>;
+  let searchServiceStub = {
+    search() { return of({
+      total: 0,
+      groupedBy: '',
+      results: [],
+      facetCounts: [],
+      groups: []
+    }) },
+    pollSearch() { return of({}) }
+  }
+  let queryBuilderStub = {
+    addOrUpdateFilter() { return {} },
+    clearSearch() { return {} },
+    generateSelect() { return '*' },
+    isTimeStampFieldPresent() { return {} },
+    filters: [{}],
+    searchRequest: {
+      from: 0
+    }
+  }
 
   let queryBuilder: QueryBuilder;
   let searchService: SearchService;
@@ -53,9 +74,7 @@ describe('AlertsListComponent', () => {
         AlertsListComponent,
       ],
       providers: [
-        { provide: SearchService, useClass: () => { return {
-          search: () => {},
-        } } },
+        { provide: SearchService, useValue: searchServiceStub },
         { provide: UpdateService, useClass: () => { return {
           alertChanged$: new Observable(),
         } } },
@@ -77,9 +96,7 @@ describe('AlertsListComponent', () => {
           get: () => new Observable(),
         } } },
         { provide: DialogService, useClass: () => { return {} } },
-        { provide: QueryBuilder, useClass: () => { return {
-          addOrUpdateFilter: () => {}
-        } } },
+        { provide: QueryBuilder, useValue: queryBuilderStub },
       ]
     })
     .compileComponents();
@@ -122,6 +139,62 @@ describe('AlertsListComponent', () => {
     expect(fixture.nativeElement.querySelector('[data-qe-id="alert-subgroup-total"]')).toBeNull();
   });
 
+  it('should toggle the query builder with toggleQueryBuilder', () => {
+    component.toggleQueryBuilder();
+    fixture.detectChanges();
+    expect(component.hideQueryBuilder).toBe(true);
+
+    component.hideQueryBuilder = true;
+    component.pagination.from = 0;
+    component.pagination.size = 25;
+
+    fixture.detectChanges();
+    component.toggleQueryBuilder();
+    expect(component.hideQueryBuilder).toBe(false);
+  });
+
+  it('should pass the manual query value when hideQueryBuilder is true', () => {
+    const input = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]'));
+    const el = input.nativeElement;
+
+    expect(component.queryForTreeView()).toBe('*');
+
+    component.toggleQueryBuilder();
+    fixture.detectChanges();
+    expect(component.hideQueryBuilder).toBe(true);
+
+    el.value = 'test';
+    expect(component.queryForTreeView()).toBe('test');
+  });
+
+  it('should build a new search request if hideQueryBuilder is true', () => {
+    const input = fixture.debugElement.query(By.css('[data-qe-id="manual-query-input"]'));
+    const el = input.nativeElement;
+    const searchServiceSpy = spyOn(searchService, 'search').and.returnValue(of());
+    const newSearch = new SearchRequest();
+
+    el.value = 'test';
+    component.hideQueryBuilder = true;
+    component.pagination.size = 25;
+    newSearch.query = 'test'
+    newSearch.size = 25
+    newSearch.from = 0;
+
+    fixture.detectChanges();
+    component.search();
+    expect(searchServiceSpy).toHaveBeenCalledWith(newSearch);
+  });
+
+  it('should poll with new search request if isRefreshPaused is true and manualSearch is present', () => {
+    const searchServiceSpy = spyOn(searchService, 'pollSearch').and.returnValue(of());
+    const newSearch = new SearchRequest();
+
+    component.isRefreshPaused = false;
+    fixture.detectChanges();
+    component.tryStartPolling(newSearch);
+    expect(searchServiceSpy).toHaveBeenCalledWith(newSearch);
+  });
+
   describe('stale data state', () => {
 
     it('should set staleDataState flag to true on filter change', () => {
@@ -145,7 +218,6 @@ describe('AlertsListComponent', () => {
     });
 
     it('should set staleDataState flag to false when the query resolves', () => {
-      const fakeObservable = new Subject();
       spyOn(searchService, 'search').and.returnValue(of(new SearchResponse()));
       spyOn(component, 'saveCurrentSearch');
       spyOn(component, 'setSearchRequestSize');
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
index 61f57c2..2cd34a5 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
@@ -46,6 +46,7 @@ import { DialogService } from 'app/service/dialog.service';
 import { DialogType } from 'app/model/dialog-type';
 import { Utils } from 'app/utils/utils';
 import { AlertSource } from '../../model/alert-source';
+import { SearchRequest } from 'app/model/search-request';
 
 @Component({
   selector: 'app-alerts-list',
@@ -74,6 +75,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   @ViewChild('table') table: ElementRef;
   @ViewChild('dataViewComponent') dataViewComponent: TableViewComponent;
   @ViewChild(AlertSearchDirective) alertSearchDirective: AlertSearchDirective;
+  @ViewChild('manualQuery') manualQuery: ElementRef;
 
   tableMetaData = new TableMetadata();
   pagination: Pagination = new Pagination();
@@ -83,6 +85,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   configSubscription: Subscription;
   groups = [];
   subgroupTotal = 0;
+  hideQueryBuilder = false;
 
   staleDataState = false;
 
@@ -223,6 +226,8 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   onClear() {
     this.timeStampFilterPresent = false;
     this.queryBuilder.clearSearch();
+    if (this.hideQueryBuilder) { this.manualQuery.nativeElement.value = '*'; }
+    this.search();
     this.staleDataState = true;
   }
 
@@ -261,7 +266,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   }
 
   onAddFilter(filter: Filter) {
-    this.timeStampFilterPresent = (filter.field === TIMESTAMP_FIELD_NAME);
+    this.timeStampFilterPresent = this.queryBuilder.isTimeStampFieldPresent();
     this.queryBuilder.addOrUpdateFilter(filter);
     this.staleDataState = true;
   }
@@ -374,22 +379,39 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   }
 
   search(resetPaginationParams = true, savedSearch?: SaveSearch) {
-    this.saveCurrentSearch(savedSearch);
+    if (savedSearch) { this.saveCurrentSearch(savedSearch); }
     if (resetPaginationParams) {
       this.pagination.from = 0;
     }
 
     this.setSearchRequestSize();
 
-    this.searchService.search(this.queryBuilder.searchRequest).subscribe(results => {
-      this.setData(results);
-      this.staleDataState = false;
-    }, error => {
-      this.setData(new SearchResponse());
-      this.dialogService.launchDialog(ElasticsearchUtils.extractESErrorMessage(error), DialogType.Error);
-    });
+    if (this.hideQueryBuilder) {
+      const newSearch = new SearchRequest();
+      newSearch.query = this.manualQuery.nativeElement.value;
+      newSearch.size = this.pagination.size;
+      newSearch.from = 0;
 
-    this.tryStartPolling();
+      this.searchService.search(newSearch).subscribe(results => {
+        this.setData(results);
+        this.staleDataState = false;
+      }, error => {
+        this.setData(new SearchResponse());
+        this.dialogService.launchDialog(ElasticsearchUtils.extractESErrorMessage(error), DialogType.Error);
+      });
+
+      this.tryStartPolling(newSearch);
+    } else {
+        this.searchService.search(this.queryBuilder.searchRequest).subscribe(results => {
+        this.setData(results);
+        this.staleDataState = false;
+      }, error => {
+        this.setData(new SearchResponse());
+        this.dialogService.launchDialog(ElasticsearchUtils.extractESErrorMessage(error), DialogType.Error);
+      });
+
+      this.tryStartPolling();
+    }
   }
 
   setSearchRequestSize() {
@@ -477,12 +499,17 @@ export class AlertsListComponent implements OnInit, OnDestroy {
     this.router.navigateByUrl('/alerts-list(dialog:save-search)');
   }
 
-  tryStartPolling() {
-    if (!this.isRefreshPaused) {
+  tryStartPolling(manualSearch?: SearchRequest) {
+    if (!this.isRefreshPaused && !manualSearch) {
       this.tryStopPolling();
       this.refreshTimer = this.searchService.pollSearch(this.queryBuilder.searchRequest).subscribe(results => {
         this.setData(results);
       });
+    } else if (!this.isRefreshPaused && manualSearch) {
+      this.tryStopPolling();
+      this.refreshTimer = this.searchService.pollSearch(manualSearch).subscribe(results => {
+        this.setData(results);
+      });
     }
   }
 
@@ -521,4 +548,25 @@ export class AlertsListComponent implements OnInit, OnDestroy {
     this.subgroupTotal = subgroupTotal;
     this.cdRef.detectChanges();
   }
+
+  toggleQueryBuilder() {
+    this.setSelectedTimeRange([this.selectedTimeRange]);
+    if (!this.hideQueryBuilder) {
+      this.hideQueryBuilder = true;
+      this.manualQuery.nativeElement.value = this.queryBuilder.query;
+    } else {
+      this.hideQueryBuilder = false;
+      this.queryBuilder.clearSearch();
+      this.search();
+    }
+  }
+
+  queryForTreeView() {
+    if (!this.hideQueryBuilder) {
+      return this.queryBuilder.generateSelect();
+    } else {
+      return this.manualQuery.nativeElement.value;
+    }
+  }
+
 }
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.spec.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.spec.ts
index 29c0ffe..604359b 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.spec.ts
@@ -33,6 +33,7 @@ import { MetaAlertService } from '../../../service/meta-alert.service';
 import { DialogService } from 'app/service/dialog.service';
 import { AppConfigService } from '../../../service/app-config.service';
 import { ContextMenuComponent } from 'app/shared/context-menu/context-menu.component';
+import { of } from 'rxjs';
 
 @Component({selector: 'metron-table-pagination', template: ''})
 class MetronTablePaginationComponent {
@@ -57,10 +58,12 @@ describe('TableViewComponent', () => {
       providers: [
         SearchService,
         UpdateService,
-        GlobalConfigService,
         MetaAlertService,
         DialogService,
-        { provide: AppConfigService, useClass: FakeAppConfigService }
+        { provide: AppConfigService, useClass: FakeAppConfigService },
+        { provide: GlobalConfigService, useValue: {
+          get: () => { return of({})}
+        }}
       ],
       declarations: [
         MetronTableDirective,
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.spec.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.spec.ts
index cdea081..0d33eba 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.spec.ts
@@ -33,6 +33,7 @@ import { GlobalConfigService } from '../../../service/global-config.service';
 import { MetaAlertService } from '../../../service/meta-alert.service';
 import { DialogService } from 'app/service/dialog.service';
 import { AppConfigService } from '../../../service/app-config.service';
+import { of } from 'rxjs';
 
 class FakeAppConfigService {
 
@@ -51,10 +52,12 @@ describe('TreeViewComponent', () => {
       providers: [
         SearchService,
         UpdateService,
-        GlobalConfigService,
         MetaAlertService,
         DialogService,
-        { provide: AppConfigService, useClass: FakeAppConfigService }
+        { provide: AppConfigService, useClass: FakeAppConfigService },
+        { provide: GlobalConfigService, useValue: {
+          get: () => { return of({})}
+        }}
       ],
       declarations: [
         MetronTableDirective,