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);