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,