METRON-2205 Cease querying on filter or time-range change (tiborm via sardell) closes apache/metron#1478
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 4ed3951..721426d 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
@@ -76,6 +76,10 @@
<app-alert-filters [facets]="searchResponse.facetCounts" (facetFilterChange)="onAddFacetFilter($event)"> </app-alert-filters>
</div>
<div class="col px-0 pl-4" style="overflow: auto;">
+ <div class="alert alert-warning" role="alert" *ngIf="staleDataState" data-qe-id="staleDataWarning">
+ <i class="fa fa-warning" aria-hidden="true"></i> Data is in a stale state! Click
+ <i class="fa fa-search" aria-hidden="true"></i> to update your view based on your current filter and time-range configuration!
+ </div>
<div class="col-xs-12 pl-0 pb-3">
<app-group-by [facets]="groupFacets" (groupsChange)="onGroupsChange($event)"> </app-group-by>
</div>
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 6779baa..7fbf9cc 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,15 +28,21 @@
import { MetaAlertService } from 'app/service/meta-alert.service';
import { GlobalConfigService } from 'app/service/global-config.service';
import { DialogService } from 'app/service/dialog.service';
-import { Observable } from 'rxjs';
+import { Observable, of, Subject } from 'rxjs';
import { Filter } from 'app/model/filter';
import { QueryBuilder } from './query-builder';
+import { TIMESTAMP_FIELD_NAME } from 'app/utils/constants';
+import { SearchResponse } from 'app/model/search-response';
+import { By } from '@angular/platform-browser';
describe('AlertsListComponent', () => {
let component: AlertsListComponent;
let fixture: ComponentFixture<AlertsListComponent>;
+ let queryBuilder: QueryBuilder;
+ let searchService: SearchService;
+
beforeEach(async(() => {
TestBed.configureTestingModule({
schemas: [ NO_ERRORS_SCHEMA ],
@@ -47,7 +53,9 @@
AlertsListComponent,
],
providers: [
- { provide: SearchService, useClass: () => { return {} } },
+ { provide: SearchService, useClass: () => { return {
+ search: () => {},
+ } } },
{ provide: UpdateService, useClass: () => { return {
alertChanged$: new Observable(),
} } },
@@ -75,6 +83,9 @@
]
})
.compileComponents();
+
+ queryBuilder = TestBed.get(QueryBuilder);
+ searchService = TestBed.get(SearchService);
}));
beforeEach(() => {
@@ -111,4 +122,50 @@
expect(fixture.nativeElement.querySelector('[data-qe-id="alert-subgroup-total"]')).toBeNull();
});
+ describe('stale data state', () => {
+
+ it('should set staleDataState flag to true on filter change', () => {
+ expect(component.staleDataState).toBe(false);
+ component.onAddFilter(new Filter('ip_src_addr', '0.0.0.0'));
+ expect(component.staleDataState).toBe(true);
+ });
+
+ it('should set staleDataState flag to true on filter clearing', () => {
+ queryBuilder.clearSearch = jasmine.createSpy('clearSearch');
+
+ expect(component.staleDataState).toBe(false);
+ component.onClear();
+ expect(component.staleDataState).toBe(true);
+ });
+
+ it('should set staleDataState flag to true on timerange change', () => {
+ expect(component.staleDataState).toBe(false);
+ component.onTimeRangeChange(new Filter(TIMESTAMP_FIELD_NAME, 'this-year'));
+ expect(component.staleDataState).toBe(true);
+ });
+
+ 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');
+ spyOn(component, 'setSelectedTimeRange');
+ spyOn(component, 'createGroupFacets');
+
+ component.staleDataState = true;
+ component.search();
+ expect(component.staleDataState).toBe(false);
+ });
+
+ it('should show warning if data is in a stale state', () => {
+ expect(fixture.debugElement.query(By.css('[data-qe-id="staleDataWarning"]'))).toBe(null);
+
+ component.staleDataState = true;
+ fixture.detectChanges();
+
+ expect(fixture.debugElement.query(By.css('[data-qe-id="staleDataWarning"]'))).toBeTruthy();
+ });
+
+ })
+
});
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 049baae..61f57c2 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
@@ -84,6 +84,8 @@
groups = [];
subgroupTotal = 0;
+ staleDataState = false;
+
constructor(private router: Router,
private searchService: SearchService,
private updateService: UpdateService,
@@ -221,7 +223,7 @@
onClear() {
this.timeStampFilterPresent = false;
this.queryBuilder.clearSearch();
- this.search();
+ this.staleDataState = true;
}
onSearch(query: string) {
@@ -261,7 +263,7 @@
onAddFilter(filter: Filter) {
this.timeStampFilterPresent = (filter.field === TIMESTAMP_FIELD_NAME);
this.queryBuilder.addOrUpdateFilter(filter);
- this.search();
+ this.staleDataState = true;
}
onConfigRowsChange() {
@@ -291,7 +293,7 @@
onTimeRangeChange(filter: Filter) {
this.updateQueryBuilder(filter);
- this.search();
+ this.staleDataState = true;
}
private updateQueryBuilder(timeRangeFilter: Filter) {
@@ -381,6 +383,7 @@
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);