You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by mm...@apache.org on 2019/07/23 19:38:20 UTC

[metron] branch master updated: METRON-2140: [UI] Implement logic behind show/hide RESOLVE and DISMISS items in Alerts UI (tiborm via mmiklavc) closes apache/metron#1459

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

mmiklavcic 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 5e1e3bd  METRON-2140: [UI] Implement logic behind show/hide RESOLVE and DISMISS items in Alerts UI (tiborm via mmiklavc) closes apache/metron#1459
5e1e3bd is described below

commit 5e1e3bd2e2876a6774b9c50d72115332f0bc15ee
Author: tiborm <ti...@gmail.com>
AuthorDate: Tue Jul 23 13:35:21 2019 -0600

    METRON-2140: [UI] Implement logic behind show/hide RESOLVE and DISMISS items in Alerts UI (tiborm via mmiklavc) closes apache/metron#1459
---
 .../alerts/alerts-list/alerts-list.component.html  |  13 +-
 .../alerts-list/alerts-list.component.spec.ts      |   4 +
 .../alerts/alerts-list/alerts-list.component.ts    |  52 ++++----
 .../app/alerts/alerts-list/alerts-list.module.ts   |  89 ++++++++------
 .../src/app/alerts/alerts-list/query-builder.ts    |  20 ++--
 .../alerts-list/table-view/table-view.component.ts |  29 +++--
 .../alerts-list/tree-view/tree-view.component.ts   |  52 ++++----
 .../configure-rows/configure-rows.component.html   |   7 +-
 .../configure-rows/configure-rows.component.scss   |   2 +-
 .../configure-rows.component.spec.ts               |  14 ++-
 .../configure-rows/configure-rows.component.ts     |   5 +-
 .../alerts/configure-rows/configure-rows.module.ts |  22 ++--
 .../show-hide-alert-entries.component.spec.ts      | 132 +++++++++++++++++++++
 .../show-hide/show-hide-alert-entries.component.ts |  51 ++++++++
 .../show-hide/show-hide.service.spec.ts            | 125 +++++++++++++++++++
 .../configure-rows/show-hide/show-hide.service.ts  |  70 +++++++++++
 .../metron-alerts/src/app/app.module.ts            |   2 -
 .../src/app/service/search.service.ts              |   5 +-
 .../src/app/shared/switch/switch.component.html    |   2 +-
 .../src/app/shared/switch/switch.component.ts      |  12 +-
 .../src/app/shared/switch/switch.module.ts         |  15 ++-
 21 files changed, 558 insertions(+), 165 deletions(-)

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 26a38cb..4ed3951 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
@@ -24,7 +24,7 @@
                         <button class="btn btn-secondary btn-search-clear" type="button" (click)="onClear()"></button>
                     </span>
                     <span class="input-group-append" style="white-space: nowrap;">
-                        <app-time-range class="d-flex position-relative" (timeRangeChange)="onTimeRangeChange($event)" [disabled]="timeStampfilterPresent" [selectedTimeRange]="selectedTimeRange"> </app-time-range>
+                        <app-time-range class="d-flex position-relative" (timeRangeChange)="onTimeRangeChange($event)" [disabled]="timeStampFilterPresent" [selectedTimeRange]="selectedTimeRange"> </app-time-range>
                     </span>
                     <span class="input-group-append">
                         <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>
@@ -82,26 +82,27 @@
         <div class="col-xs-12 px-0">
             <app-table-view #dataViewComponent
                             [alerts]="alerts" *ngIf="groups.length === 0"
-                            [queryBuilder]="queryBuilder"
                             [pagination]="pagination"
                             [alertsColumnsToDisplay]="alertsColumnsToDisplay"
                             [selectedAlerts]="selectedAlerts"
                             (onResize)="onResize()"
                             (onAddFilter)="onAddFilter($event)"
-                            (onRefreshData)="onRefreshData($event)"
                             (onShowDetails)="showDetails($event)"
-                            (onSelectedAlertsChange)="onSelectedAlertsChange($event)"></app-table-view>
+                            (onSelectedAlertsChange)="onSelectedAlertsChange($event)"
+                            (onSortChanged)="onSortChanged($event)"
+                            (onPageChanged)="onPageChanged($event)"></app-table-view>
             <app-tree-view #dataViewComponent *ngIf="groups.length !== 0"
                            [alerts]="alerts"
-                           [queryBuilder]="queryBuilder"
                            [alertsColumnsToDisplay]="alertsColumnsToDisplay"
                            [selectedAlerts]="selectedAlerts"
                            [globalConfig]="globalConfig"
+                           [query]="queryBuilder.generateSelect()"
+                           [groups]="groups"
                            (onResize)="onResize()"
                            (onAddFilter)="onAddFilter($event)"
-                           (onRefreshData)="onRefreshData($event)"
                            (onShowDetails)="showDetails($event)"
                            (onSelectedAlertsChange)="onSelectedAlertsChange($event)"
+                           (onMetaAlertCreated)="search($event)"
                            (treeViewChange)="onTreeViewChange($event)"></app-tree-view>
         </div>
     </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 e922984..6779baa 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
@@ -30,6 +30,7 @@ import { GlobalConfigService } from 'app/service/global-config.service';
 import { DialogService } from 'app/service/dialog.service';
 import { Observable } from 'rxjs';
 import { Filter } from 'app/model/filter';
+import { QueryBuilder } from './query-builder';
 
 describe('AlertsListComponent', () => {
 
@@ -68,6 +69,9 @@ describe('AlertsListComponent', () => {
           get: () => new Observable(),
         } } },
         { provide: DialogService, useClass: () => { return {} } },
+        { provide: QueryBuilder, useClass: () => { return {
+          addOrUpdateFilter: () => {}
+        } } },
       ]
     })
     .compileComponents();
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 7fd69ba..47e777f 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
@@ -37,16 +37,15 @@ import {SearchResponse} from '../../model/search-response';
 import {ElasticsearchUtils} from '../../utils/elasticsearch-utils';
 import {Filter} from '../../model/filter';
 import { TIMESTAMP_FIELD_NAME, ALL_TIME, POLLING_DEFAULT_STATE } from '../../utils/constants';
-import {TableViewComponent} from './table-view/table-view.component';
+import {TableViewComponent, PageChangedEvent, SortChangedEvent} from './table-view/table-view.component';
 import {Pagination} from '../../model/pagination';
-import {META_ALERTS_SENSOR_TYPE} from '../../utils/constants';
 import {MetaAlertService} from '../../service/meta-alert.service';
 import {Facets} from '../../model/facets';
 import { GlobalConfigService } from '../../service/global-config.service';
 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 { AlertSource } from '../../model/alert-source';
 
 @Component({
   selector: 'app-alerts-list',
@@ -67,7 +66,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   isRefreshPaused = POLLING_DEFAULT_STATE;
   lastIsRefreshPausedValue = false;
   isMetaAlertPresentInSelectedAlerts = false;
-  timeStampfilterPresent = false;
+  timeStampFilterPresent = false;
 
   readonly DEFAULT_TIME_RANGE = 'last-15-minutes';
   selectedTimeRange: Filter;
@@ -77,7 +76,6 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   @ViewChild(AlertSearchDirective) alertSearchDirective: AlertSearchDirective;
 
   tableMetaData = new TableMetadata();
-  queryBuilder: QueryBuilder = new QueryBuilder();
   pagination: Pagination = new Pagination();
   alertChangedSubscription: Subscription;
   groupFacets: Facets;
@@ -96,7 +94,8 @@ export class AlertsListComponent implements OnInit, OnDestroy {
               private metaAlertsService: MetaAlertService,
               private globalConfigService: GlobalConfigService,
               private dialogService: DialogService,
-              private cdRef : ChangeDetectorRef) {
+              public queryBuilder: QueryBuilder,
+              private cdRef: ChangeDetectorRef) {
     router.events.subscribe(event => {
       if (event instanceof NavigationStart && event.url === '/alerts-list') {
         this.selectedAlerts = [];
@@ -128,14 +127,11 @@ export class AlertsListComponent implements OnInit, OnDestroy {
 
   addLoadSavedSearchListner() {
     this.saveSearchService.loadSavedSearch$.subscribe((savedSearch: SaveSearch) => {
-      let queryBuilder = new QueryBuilder();
-      queryBuilder.setGroupby(this.getGroupRequest().groups.map(group => group.field));
-      queryBuilder.searchRequest = savedSearch.searchRequest;
-      queryBuilder.filters = savedSearch.filters;
-      this.queryBuilder = queryBuilder;
+      this.queryBuilder.searchRequest = savedSearch.searchRequest;
+      this.queryBuilder.filters = savedSearch.filters;
       this.setSelectedTimeRange(savedSearch.filters);
       this.prepareColumnData(savedSearch.tableColumns, []);
-      this.timeStampfilterPresent = this.queryBuilder.isTimeStampFieldPresent();
+      this.timeStampFilterPresent = this.queryBuilder.isTimeStampFieldPresent();
       this.search(true, savedSearch);
     });
   }
@@ -223,7 +219,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   }
 
   onClear() {
-    this.timeStampfilterPresent = false;
+    this.timeStampFilterPresent = false;
     this.queryBuilder.clearSearch();
     this.selectedTimeRange = new Filter(TIMESTAMP_FIELD_NAME, ALL_TIME, false);
     this.search();
@@ -231,7 +227,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
 
   onSearch(query: string) {
     this.queryBuilder.setSearch(query);
-    this.timeStampfilterPresent = this.queryBuilder.isTimeStampFieldPresent();
+    this.timeStampFilterPresent = this.queryBuilder.isTimeStampFieldPresent();
     this.search();
     return false;
   }
@@ -240,8 +236,14 @@ export class AlertsListComponent implements OnInit, OnDestroy {
     this.onAddFilter(new Filter($event.name, $event.key));
   }
 
-  onRefreshData($event) {
-    this.search($event);
+  onSortChanged(event: SortChangedEvent) {
+    this.queryBuilder.setSort(event.sortBy, event.sortOrder);
+    this.search(true);
+  }
+
+  onPageChanged(event: PageChangedEvent) {
+    this.queryBuilder.setFromAndSize(event.from, event.size);
+    this.search(false);
   }
 
   onSelectedAlertsChange(selectedAlerts) {
@@ -258,7 +260,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   }
 
   onAddFilter(filter: Filter) {
-    this.timeStampfilterPresent = (filter.field === TIMESTAMP_FIELD_NAME);
+    this.timeStampFilterPresent = (filter.field === TIMESTAMP_FIELD_NAME);
     this.queryBuilder.addOrUpdateFilter(filter);
     this.search();
   }
@@ -295,7 +297,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
 
   private updateQueryBuilder(timeRangeFilter: Filter) {
     if (timeRangeFilter.value === ALL_TIME) {
-      this.queryBuilder.removeFilter(timeRangeFilter.field);
+      this.queryBuilder.removeFilter(timeRangeFilter);
     } else {
       this.queryBuilder.addOrUpdateFilter(timeRangeFilter);
     }
@@ -365,12 +367,6 @@ export class AlertsListComponent implements OnInit, OnDestroy {
     }
   }
 
-  removeFilter(field: string) {
-    this.timeStampfilterPresent = (field === TIMESTAMP_FIELD_NAME) ? false : this.timeStampfilterPresent;
-    this.queryBuilder.removeFilter(field);
-    this.search();
-  }
-
   restoreRefreshState() {
     this.isRefreshPaused = this.lastIsRefreshPausedValue;
     this.tryStartPolling();
@@ -394,12 +390,8 @@ export class AlertsListComponent implements OnInit, OnDestroy {
     this.tryStartPolling();
   }
 
-  getGroupRequest() {
-    return this.queryBuilder.groupRequest(this.globalConfig['threat.triage.score.field']);
-  }
-
   setSearchRequestSize() {
-    if (this.getGroupRequest().groups.length === 0) {
+    if (this.groups.length === 0) {
       this.queryBuilder.searchRequest.from = this.pagination.from;
       if (this.tableMetaData.size) {
         this.pagination.size = this.tableMetaData.size;
@@ -486,7 +478,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
   tryStartPolling() {
     if (!this.isRefreshPaused) {
       this.tryStopPolling();
-      this.refreshTimer = this.searchService.pollSearch(this.queryBuilder).subscribe(results => {
+      this.refreshTimer = this.searchService.pollSearch(this.queryBuilder.searchRequest).subscribe(results => {
         this.setData(results);
       });
     }
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.module.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.module.ts
index d1c3cc0..1126f14 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.module.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.module.ts
@@ -1,43 +1,56 @@
 /**
- * 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 {NgModule} from '@angular/core';
-import {DecimalPipe} from '@angular/common';
+* 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 { NgModule } from '@angular/core';
+import { DecimalPipe } from '@angular/common';
 
-import {AlertsListComponent}   from './alerts-list.component';
-import {routing} from './alerts-list.routing';
-import {SharedModule} from '../../shared/shared.module';
-import {MetronSorterModule} from '../../shared/metron-table/metron-sorter/metron-sorter.module';
-import {ListGroupModule} from '../../shared/list-group/list-grup.module';
-import {CollapseModule} from '../../shared/collapse/collapse.module';
-import {MetronTablePaginationModule} from '../../shared/metron-table/metron-table-pagination/metron-table-pagination.module';
-import {ConfigureRowsModule} from '../configure-rows/configure-rows.module';
-import {TimeRangeModule} from '../../shared/time-range/time-range.module';
-import {GroupByModule} from '../../shared/group-by/group-by.module';
-import {AlertFiltersComponent} from './alert-filters/alert-filters.component';
-import {TableViewComponent} from './table-view/table-view.component';
-import {TreeViewComponent} from './tree-view/tree-view.component';
+import { AlertsListComponent }   from './alerts-list.component';
+import { routing } from './alerts-list.routing';
+import { SharedModule } from '../../shared/shared.module';
+import { MetronSorterModule } from '../../shared/metron-table/metron-sorter/metron-sorter.module';
+import { ListGroupModule } from '../../shared/list-group/list-grup.module';
+import { CollapseModule } from '../../shared/collapse/collapse.module';
+import { MetronTablePaginationModule } from '../../shared/metron-table/metron-table-pagination/metron-table-pagination.module';
+import { ConfigureRowsModule } from '../configure-rows/configure-rows.module';
+import { TimeRangeModule } from '../../shared/time-range/time-range.module';
+import { GroupByModule } from '../../shared/group-by/group-by.module';
+import { AlertFiltersComponent } from './alert-filters/alert-filters.component';
+import { TableViewComponent } from './table-view/table-view.component';
+import { TreeViewComponent } from './tree-view/tree-view.component';
 
 @NgModule({
-    imports: [routing, SharedModule, ConfigureRowsModule, MetronSorterModule, MetronTablePaginationModule,
-                ListGroupModule, CollapseModule, GroupByModule, TimeRangeModule],
-    exports: [AlertsListComponent],
-    declarations: [AlertsListComponent, TableViewComponent, TreeViewComponent, AlertFiltersComponent],
-    providers: [DecimalPipe]
+  imports: [
+    routing,
+    SharedModule,
+    ConfigureRowsModule,
+    MetronSorterModule,
+    MetronTablePaginationModule,
+    ListGroupModule,
+    CollapseModule,
+    GroupByModule,
+    TimeRangeModule,
+  ],
+  exports: [ AlertsListComponent ],
+  declarations: [
+    AlertsListComponent,
+    TableViewComponent,
+    TreeViewComponent,
+    AlertFiltersComponent
+  ],
+  providers: [ DecimalPipe ]
 })
-export class AlertsListModule {
-}
+export class AlertsListModule {}
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/query-builder.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/query-builder.ts
index 6cbed25..f9c9b70 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/query-builder.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/query-builder.ts
@@ -22,7 +22,9 @@ import {SortField} from '../../model/sort-field';
 import {TIMESTAMP_FIELD_NAME} from '../../utils/constants';
 import {GroupRequest} from '../../model/group-request';
 import {Group} from '../../model/group';
+import { Injectable } from '@angular/core';
 
+@Injectable()
 export class QueryBuilder {
   private _searchRequest = new SearchRequest();
   private _groupRequest = new GroupRequest();
@@ -48,7 +50,6 @@ export class QueryBuilder {
     return this._filters;
   }
 
-
   get searchRequest(): SearchRequest {
     this._searchRequest.query = this.generateSelect();
     return this._searchRequest;
@@ -78,9 +79,11 @@ export class QueryBuilder {
   addOrUpdateFilter(filter: Filter) {
     let existingFilterIndex = -1;
 
-    // only one timerange filter applicable
     if (filter.field === TIMESTAMP_FIELD_NAME) {
-      this.removeFilter(filter.field);
+      const existingTimeRangeFilter = this.filters.find(fItem => fItem.field === TIMESTAMP_FIELD_NAME);
+      if (existingTimeRangeFilter) {
+        this.removeFilter(existingTimeRangeFilter);
+      }
       this._filters.push(filter);
       this.onSearchChange();
       return;
@@ -136,11 +139,12 @@ export class QueryBuilder {
     this._displayQuery = this.generateSelectForDisplay();
   }
 
-  removeFilter(field: string) {
-    let filter = this._filters.find(tFilter => tFilter.field === field);
-    this._filters.splice(this._filters.indexOf(filter), 1);
-
-    this.onSearchChange();
+  removeFilter(filter: Filter) {
+    const filterIndex = this._filters.indexOf(filter);
+    if (filterIndex >= 0) {
+      this._filters.splice(filterIndex, 1);
+      this.onSearchChange();
+    }
   }
 
   setFields(fieldNames: string[]) {
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.ts
index 6f4bc9f..4092193 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.ts
@@ -24,7 +24,6 @@ import {SortEvent} from '../../../shared/metron-table/metron-table.directive';
 import {ColumnMetadata} from '../../../model/column-metadata';
 import {Alert} from '../../../model/alert';
 import {SearchService} from '../../../service/search.service';
-import {QueryBuilder} from '../query-builder';
 import {Sort} from '../../../utils/enums';
 import {Filter} from '../../../model/filter';
 import {AlertSource} from '../../../model/alert-source';
@@ -35,7 +34,7 @@ import {GetRequest} from '../../../model/get-request';
 import { GlobalConfigService } from '../../../service/global-config.service';
 import { DialogService } from '../../../service/dialog.service';
 import { ConfirmationType } from 'app/model/confirmation-type';
-import {HttpErrorResponse} from "@angular/common/http";
+import {HttpErrorResponse} from '@angular/common/http';
 
 import { merge } from '../../../shared/context-menu/context-menu.util'
 
@@ -43,6 +42,16 @@ export enum MetronAlertDisplayState {
   COLLAPSE, EXPAND
 }
 
+export interface SortChangedEvent {
+  sortBy: string;
+  sortOrder: string;
+}
+
+export interface PageChangedEvent {
+  from: number;
+  size: number;
+}
+
 @Component({
   selector: 'app-table-view',
   templateUrl: './table-view.component.html',
@@ -60,14 +69,14 @@ export class TableViewComponent implements OnInit, OnChanges, OnDestroy {
   merge: Function = merge;
 
   @Input() alerts: Alert[] = [];
-  @Input() queryBuilder: QueryBuilder;
   @Input() pagination: Pagination;
   @Input() alertsColumnsToDisplay: ColumnMetadata[] = [];
   @Input() selectedAlerts: Alert[] = [];
 
   @Output() onResize = new EventEmitter<void>();
   @Output() onAddFilter = new EventEmitter<Filter>();
-  @Output() onRefreshData = new EventEmitter<boolean>();
+  @Output() onSortChanged = new EventEmitter<SortChangedEvent>();
+  @Output() onPageChanged = new EventEmitter<PageChangedEvent>();
   @Output() onShowDetails = new EventEmitter<Alert>();
   @Output() onShowConfigureTable = new EventEmitter<Alert>();
   @Output() onSelectedAlertsChange = new EventEmitter< Alert[]>();
@@ -112,11 +121,7 @@ export class TableViewComponent implements OnInit, OnChanges, OnDestroy {
   }
 
   hasScore(alertSource) {
-    if (alertSource[this.threatScoreFieldName()]) {
-      return true;
-    } else {
-      return false;
-    }
+    return !!this.getScore(alertSource);
   }
 
   getScore(alertSource) {
@@ -146,8 +151,7 @@ export class TableViewComponent implements OnInit, OnChanges, OnDestroy {
   onSort(sortEvent: SortEvent) {
     let sortOrder = (sortEvent.sortOrder === Sort.ASC ? 'asc' : 'desc');
     let sortBy = sortEvent.sortBy === 'id' ? '_uid' : sortEvent.sortBy;
-    this.queryBuilder.setSort(sortBy, sortOrder);
-    this.onRefreshData.emit(true);
+    this.onSortChanged.emit({ sortBy, sortOrder });
   }
 
   getValue(alert: Alert, column: ColumnMetadata, formatData: boolean) {
@@ -193,8 +197,7 @@ export class TableViewComponent implements OnInit, OnChanges, OnDestroy {
   }
 
   onPageChange() {
-    this.queryBuilder.setFromAndSize(this.pagination.from, this.pagination.size);
-    this.onRefreshData.emit(false);
+    this.onPageChanged.emit({ from: this.pagination.from, size: this.pagination.size });
   }
 
   selectRow($event, alert: Alert) {
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.ts b/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.ts
index f3833d3..3bd0055 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/tree-view/tree-view.component.ts
@@ -33,13 +33,15 @@ import {MetaAlertCreateRequest} from '../../../model/meta-alert-create-request';
 import {MetaAlertService} from '../../../service/meta-alert.service';
 import {INDEXES, MAX_ALERTS_IN_META_ALERTS} from '../../../utils/constants';
 import {UpdateService} from '../../../service/update.service';
-import {PatchRequest} from '../../../model/patch-request';
 import {GetRequest} from '../../../model/get-request';
 import { GlobalConfigService } from '../../../service/global-config.service';
 import { DialogService } from '../../../service/dialog.service';
 import { DialogType } from 'app/model/dialog-type';
 import { ConfirmationType } from 'app/model/confirmation-type';
-import {AlertSource} from "../../../model/alert-source";
+import { AlertSource } from '../../../model/alert-source';
+import { QueryBuilder } from '../query-builder';
+import { GroupRequest } from 'app/model/group-request';
+import { Group } from 'app/model/group';
 
 @Component({
   selector: 'app-tree-view',
@@ -50,6 +52,11 @@ import {AlertSource} from "../../../model/alert-source";
 export class TreeViewComponent extends TableViewComponent implements OnInit, OnChanges, OnDestroy {
 
   @Input() globalConfig: {} = {};
+  @Input() query = '';
+  @Input() groups: string[] = [];
+
+  @Output() onMetaAlertCreated = new EventEmitter<boolean>();
+
   @Output() treeViewChange = new EventEmitter<number>();
   groupByFields: string[] = [];
   topGroups: TreeGroupData[] = [];
@@ -90,7 +97,7 @@ export class TreeViewComponent extends TableViewComponent implements OnInit, OnC
   }
 
   createQuery(selectedGroup: TreeGroupData) {
-    let searchQuery = this.queryBuilder.generateSelect();
+    let searchQuery = this.query;
     let groupQery = Object.keys(selectedGroup.groupQueryMap).map(key => {
       return key.replace(/:/g, '\\:') +
           ':' +
@@ -125,10 +132,7 @@ export class TreeViewComponent extends TableViewComponent implements OnInit, OnC
   }
 
   getGroups() {
-    let groupRequest = this.getGroupRequest();
-    groupRequest.query = this.queryBuilder.generateSelect();
-
-    this.searchService.groups(groupRequest).subscribe(groupResponse => {
+    this.searchService.groups(this.getGroupRequest()).subscribe(groupResponse => {
       this.updateGroupData(groupResponse);
     });
   }
@@ -166,7 +170,7 @@ export class TreeViewComponent extends TableViewComponent implements OnInit, OnC
   }
 
   initTopGroups() {
-    let groupByFields =  this.getGroupRequest().groups.map(group => group.field);
+    let groupByFields =  this.groups;
     let currentTopGroupKeys = this.groupResponse.groupResults.map(groupResult => groupResult.key);
     let previousTopGroupKeys = this.topGroups.map(group => group.key);
 
@@ -368,7 +372,7 @@ export class TreeViewComponent extends TableViewComponent implements OnInit, OnC
 
   canCreateMetaAlert(count: number) {
     if (count > MAX_ALERTS_IN_META_ALERTS) {
-      let errorMessage = 'Meta Alert cannot have more than ' + MAX_ALERTS_IN_META_ALERTS +' alerts within it';
+      let errorMessage = 'Meta Alert cannot have more than ' + MAX_ALERTS_IN_META_ALERTS + ' alerts within it';
       this.dialogService.launchDialog(errorMessage, DialogType.Error);
       return false;
     }
@@ -381,7 +385,6 @@ export class TreeViewComponent extends TableViewComponent implements OnInit, OnC
   }
 
   getAllAlertsForSlectedGroup(group: TreeGroupData): Observable<SearchResponse> {
-    let dashRowKey = Object.keys(group.groupQueryMap);
     let searchRequest = new SearchRequest();
     searchRequest.fields = ['guid', this.globalConfig['source.type.field']];
     searchRequest.from = 0;
@@ -397,37 +400,24 @@ export class TreeViewComponent extends TableViewComponent implements OnInit, OnC
       if (this.canCreateMetaAlert(searchResponse.total)) {
         let metaAlert = new MetaAlertCreateRequest();
         metaAlert.alerts = this.createGetRequestArray(searchResponse);
-        metaAlert.groups = this.getGroupRequest().groups.map(grp => grp.field);
+        metaAlert.groups = this.groups;
 
         this.metaAlertService.create(metaAlert).subscribe(() => {
-          setTimeout(() => this.onRefreshData.emit(true), 1000);
+          setTimeout(() => this.onMetaAlertCreated.emit(true), 1000);
           console.log('Meta alert created successfully');
         });
       }
     });
   }
 
-  hasScore(alertSource) {
-    if(alertSource[this.threatScoreFieldName()]) {
-      return true;
-    }
-    else {
-      return false;
-    }
-  }
-
-  getScore(alertSource) {
-    return alertSource[this.threatScoreFieldName()];
-  }
-
-  threatScoreFieldName() {
-    return this.globalConfig['threat.triage.score.field'];
+  getGroupRequest(): GroupRequest {
+    const req = new GroupRequest();
+    req.groups = this.groups.map(groupName => new Group(groupName));
+    req.query = this.query;
+    req.scoreField = this.threatScoreFieldName();
+    return req;
   }
 
-  getGroupRequest() {
-    return this.queryBuilder.groupRequest(this.threatScoreFieldName());
-    }
-
   createMetaAlert($event, group: TreeGroupData, index: number) {
     if (this.canCreateMetaAlert(group.total)) {
       let confirmationMsg = 'Do you wish to create a meta alert with ' +
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.html b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.html
index ea08736..38a0967 100644
--- a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.html
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.html
@@ -37,9 +37,8 @@
         <div class="preset-cell" [ngClass]="{'is-active': tableMetadata.size===1000}">  1000  </div>
       </div>
 
-      <app-switch [text]="'HIDE Resolved Alerts'"> </app-switch>
-      <app-switch [text]="'HIDE Dismissed Alerts'"> </app-switch>
-
-    </form>
+      <label> HIDE ALERT ENTRIES </label>
+      <app-show-hide-alert-entries (changed)="configRowsChange.emit($event)" ></app-show-hide-alert-entries>
+      </form>
   </div>
 </div>
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.scss b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.scss
index 7d16a4f..bc02e01 100644
--- a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.scss
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.scss
@@ -78,7 +78,7 @@ label {
 .fa-sort-asc {
   position: absolute;
   bottom: -50px;
-  left: 44px;
+  left: 38px;
   font-size: 62px;
   color: #333333;
   z-index: 2;
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.spec.ts b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.spec.ts
index b4a307f..533483b 100644
--- a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.spec.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.spec.ts
@@ -20,7 +20,8 @@ import { Injectable } from '@angular/core';
 
 import { ConfigureRowsComponent } from './configure-rows.component';
 import { ConfigureTableService } from '../../service/configure-table.service';
-import { SwitchComponent } from '../../shared/switch/switch.component';
+import { ShowHideAlertEntriesComponent } from './show-hide/show-hide-alert-entries.component';
+import { SwitchComponent } from 'app/shared/switch/switch.component';
 
 @Injectable()
 class ConfigureTableServiceStub {}
@@ -31,12 +32,13 @@ describe('ConfigureRowsComponent', () => {
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
-      declarations: [ 
+      declarations: [
         ConfigureRowsComponent,
-        SwitchComponent
-     ],
-      providers: [ 
-        { provide: ConfigureTableService, useValue: ConfigureTableServiceStub }  
+        ShowHideAlertEntriesComponent,
+        SwitchComponent,
+      ],
+      providers: [
+        { provide: ConfigureTableService, useValue: ConfigureTableServiceStub }
       ]
     })
     .compileComponents();
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.ts b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.ts
index d5fb44d..f643e51 100644
--- a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.component.ts
@@ -16,8 +16,8 @@
  * limitations under the License.
  */
 import { Component, Input, HostListener, ElementRef, Output, EventEmitter } from '@angular/core';
-import {TableMetadata} from '../../model/table-metadata';
-import {ConfigureTableService} from '../../service/configure-table.service';
+import { TableMetadata } from '../../model/table-metadata';
+import { ConfigureTableService } from '../../service/configure-table.service';
 
 @Component({
   selector: 'app-configure-rows',
@@ -90,6 +90,7 @@ export class ConfigureRowsComponent  {
     this.configRowsChange.emit();
     this.saveSettings();
   }
+
   onRefreshIntervalChange($event, parentElement) {
     parentElement.querySelector('.is-active').classList.remove('is-active');
     $event.target.classList.add('is-active');
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.module.ts b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.module.ts
index d011651..89585c1 100644
--- a/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.module.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/configure-rows.module.ts
@@ -16,13 +16,21 @@
  * limitations under the License.
  */
 import { NgModule } from '@angular/core';
-import {SharedModule} from '../../shared/shared.module';
-import {ConfigureRowsComponent} from './configure-rows.component';
-import {SwitchModule} from '../../shared/switch/switch.module';
+import { SharedModule } from '../../shared/shared.module';
+import { ConfigureRowsComponent } from './configure-rows.component';
+import { ShowHideAlertEntriesComponent } from './show-hide/show-hide-alert-entries.component';
+import { SwitchModule } from 'app/shared/switch/switch.module';
+import { QueryBuilder } from '../alerts-list/query-builder';
+import { ShowHideService } from './show-hide/show-hide.service';
 
-@NgModule ({
+@NgModule({
     imports: [ SharedModule, SwitchModule ],
-    declarations: [ ConfigureRowsComponent ],
-    exports: [  ConfigureRowsComponent ]
+    declarations: [ ConfigureRowsComponent, ShowHideAlertEntriesComponent ],
+    exports: [ ConfigureRowsComponent ],
+    providers: [ QueryBuilder, ShowHideService ],
 })
-export class ConfigureRowsModule { }
+export class ConfigureRowsModule {
+
+    constructor(private showHideService: ShowHideService) {}
+
+}
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide-alert-entries.component.spec.ts b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide-alert-entries.component.spec.ts
new file mode 100644
index 0000000..3539d07
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide-alert-entries.component.spec.ts
@@ -0,0 +1,132 @@
+/**
+ * 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 { ShowHideAlertEntriesComponent, ShowHideChanged } from './show-hide-alert-entries.component';
+import { ComponentFixture, async, TestBed } from '@angular/core/testing';
+import { SwitchComponent } from 'app/shared/switch/switch.component';
+import { By } from '@angular/platform-browser';
+import { Spy } from 'jasmine-core';
+import { ShowHideService } from './show-hide.service';
+
+describe('ShowHideAlertEntriesComponent', () => {
+
+  let component: ShowHideAlertEntriesComponent;
+  let fixture: ComponentFixture<ShowHideAlertEntriesComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        ShowHideAlertEntriesComponent,
+        SwitchComponent
+      ],
+      providers: [
+        { provide: ShowHideService, useClass: () => {
+          return {
+            hideDismissed: false,
+            hideResolved: false,
+            setFilterFor: jasmine.createSpy('setFilterFor')
+          }
+        } },
+      ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ShowHideAlertEntriesComponent);
+    component = fixture.componentInstance;
+  });
+
+  it('should have ShowHideService injected', () => {
+    expect(component.showHideService).toBeTruthy();
+  });
+
+  it('should have ShowHideService.hideDismissed bounded to the dismissed toggle', () => {
+    expect(fixture.debugElement.query(By.css('[data-qe-id="hideResolvedAlertsToggle"] input')).nativeElement.checked).toBe(false);
+
+    component.showHideService.hideResolved = true;
+    fixture.detectChanges();
+
+    expect(fixture.debugElement.query(By.css('[data-qe-id="hideResolvedAlertsToggle"] input')).nativeElement.checked).toBe(true);
+  });
+
+  it('should have ShowHideService.hideResolved bounded to the resolved toggle', () => {
+    expect(fixture.debugElement.query(By.css('[data-qe-id="hideDismissedAlertsToggle"] input')).nativeElement.checked).toBe(false);
+
+    component.showHideService.hideDismissed = true;
+    fixture.detectChanges();
+
+    expect(fixture.debugElement.query(By.css('[data-qe-id="hideDismissedAlertsToggle"] input')).nativeElement.checked).toBe(true);
+  });
+
+  it('should listen to change event on hide resolved toggle', () => {
+    fixture.detectChanges(); // triggering ngInit to not disturb this test
+    spyOn(component, 'onVisibilityChanged');
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideResolvedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    // it set true by localStorage.getItem, so after first click is false
+    expect(component.onVisibilityChanged).toHaveBeenCalledWith('RESOLVE', true);
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideResolvedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    expect(component.onVisibilityChanged).toHaveBeenCalledWith('RESOLVE', false);
+  });
+
+  it('should listen to change event on hide dismissed toggle', () => {
+    fixture.detectChanges(); // triggering ngInit to not disturb this test
+    spyOn(component, 'onVisibilityChanged');
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideDismissedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    expect(component.onVisibilityChanged).toHaveBeenCalledWith('DISMISS', true);
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideDismissedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    expect(component.onVisibilityChanged).toHaveBeenCalledWith('DISMISS', false);
+  });
+
+  it('should trigger changed event on any toggle changes', () => {
+    spyOn(component.changed, 'emit');
+    fixture.detectChanges();
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideDismissedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    expect((component.changed.emit as Spy).calls.argsFor(0)[0]).toEqual(new ShowHideChanged('DISMISS', true));
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideResolvedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    expect((component.changed.emit as Spy).calls.argsFor(1)[0]).toEqual(new ShowHideChanged('RESOLVE', true));
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideDismissedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    expect((component.changed.emit as Spy).calls.argsFor(2)[0]).toEqual(new ShowHideChanged('DISMISS', false));
+
+    fixture.debugElement.query(By.css('[data-qe-id="hideResolvedAlertsToggle"] input')).nativeElement.click();
+    fixture.detectChanges();
+
+    expect((component.changed.emit as Spy).calls.argsFor(3)[0]).toEqual(new ShowHideChanged('RESOLVE', false));
+  })
+
+});
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide-alert-entries.component.ts b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide-alert-entries.component.ts
new file mode 100644
index 0000000..9076282
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide-alert-entries.component.ts
@@ -0,0 +1,51 @@
+/**
+ * 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, Output, EventEmitter } from '@angular/core';
+import { ShowHideService } from './show-hide.service';
+
+export class ShowHideChanged {
+  value: string;
+  isHide: boolean;
+
+  constructor(value: string, isHide: boolean) {
+    this.value = value;
+    this.isHide = isHide;
+  }
+}
+
+@Component({
+  selector: 'app-show-hide-alert-entries',
+  template: `
+    <app-switch [text]="'HIDE Resolved Alerts'" data-qe-id="hideResolvedAlertsToggle" [selected]="showHideService.hideResolved"
+      (onChange)="onVisibilityChanged('RESOLVE', $event)"> </app-switch>
+    <app-switch [text]="'HIDE Dismissed Alerts'" data-qe-id="hideDismissedAlertsToggle" [selected]="showHideService.hideDismissed"
+      (onChange)="onVisibilityChanged('DISMISS', $event)"> </app-switch>
+  `
+})
+export class ShowHideAlertEntriesComponent {
+
+  @Output() changed = new EventEmitter<ShowHideChanged>();
+
+  constructor(public showHideService: ShowHideService) {}
+
+  onVisibilityChanged(alertStatus, isHide) {
+    this.showHideService.setFilterFor(alertStatus, isHide);
+    this.changed.emit(new ShowHideChanged(alertStatus, isHide));
+  }
+
+}
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide.service.spec.ts b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide.service.spec.ts
new file mode 100644
index 0000000..b234a3d
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide.service.spec.ts
@@ -0,0 +1,125 @@
+/**
+ * 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 { TestBed, inject, getTestBed } from '@angular/core/testing';
+
+import { ShowHideService } from './show-hide.service';
+import { QueryBuilder } from 'app/alerts/alerts-list/query-builder';
+
+import { Spy } from 'jasmine-core';
+import { Filter } from 'app/model/filter';
+
+class QueryBuilderMock {
+  addOrUpdateFilter = () => {};
+  removeFilter = () => {};
+}
+
+describe('ShowHideService', () => {
+  let queryBuilderMock: QueryBuilderMock;
+
+  beforeEach(() => {
+    spyOn(localStorage, 'getItem').and.returnValues('true', 'false');
+    spyOn(localStorage, 'setItem');
+
+    spyOn(ShowHideService.prototype, 'setFilterFor').and.callThrough();
+
+    TestBed.configureTestingModule({
+      providers: [
+        ShowHideService,
+        { provide: QueryBuilder, useClass: QueryBuilderMock },
+      ]
+    });
+
+    queryBuilderMock = getTestBed().get(QueryBuilder);
+  });
+
+  it('should be created', inject([ShowHideService], (service: ShowHideService) => {
+    expect(service).toBeTruthy();
+  }));
+
+  it('should have QueryBuilder injected', inject([ShowHideService], (service: ShowHideService) => {
+    expect(service.queryBuilder).toBeTruthy();
+  }));
+
+  it('should get persisted state from localStorage', inject([ShowHideService], (service: ShowHideService) => {
+    expect(localStorage.getItem).toHaveBeenCalledWith(service.HIDE_RESOLVE_STORAGE_KEY);
+    expect(localStorage.getItem).toHaveBeenCalledWith(service.HIDE_DISMISS_STORAGE_KEY);
+  }));
+
+  it('should set initial filter state', inject([ShowHideService], (service: ShowHideService) => {
+    expect((service.setFilterFor as Spy).calls.argsFor(0)[1]).toBe(true);
+    expect((service.setFilterFor as Spy).calls.argsFor(0)[0]).toBe('RESOLVE');
+    expect((service.setFilterFor as Spy).calls.argsFor(1)[0]).toBe('DISMISS');
+    expect((service.setFilterFor as Spy).calls.argsFor(1)[1]).toBe(false);
+  }));
+
+  it('should set value loaded from localStorage to hideDismissed ', inject([ShowHideService], (service: ShowHideService) => {
+    expect(service.hideDismissed).toBe(false);
+  }));
+
+  it('should set value loaded from localStorage to hideResolved', inject([ShowHideService], (service: ShowHideService) => {
+    expect(service.hideResolved).toBe(true);
+  }));
+
+  it('should save state to localStorage on change for RESOLVE', inject([ShowHideService], (service: ShowHideService) => {
+    service.setFilterFor('RESOLVE', true);
+
+    expect(localStorage.setItem).toHaveBeenCalledWith(service.HIDE_RESOLVE_STORAGE_KEY, true);
+  }));
+
+  it('should save state to localStorage on change for DISMISS', inject([ShowHideService], (service: ShowHideService) => {
+    service.setFilterFor('DISMISS', true);
+
+    expect(localStorage.setItem).toHaveBeenCalledWith(service.HIDE_DISMISS_STORAGE_KEY, true);
+  }));
+
+  it('should be able to add RESOLVE filter to QueryBuilder', inject([ShowHideService], (service: ShowHideService) => {
+    spyOn(queryBuilderMock, 'addOrUpdateFilter');
+    spyOn(queryBuilderMock, 'removeFilter');
+
+    service.setFilterFor('RESOLVE', true);
+    expect(queryBuilderMock.addOrUpdateFilter).toHaveBeenCalledWith(new Filter('-alert_status', 'RESOLVE', false));
+    expect(queryBuilderMock.removeFilter).not.toHaveBeenCalled();
+  }));
+
+  it('should be able to remove RESOLVE filter to QueryBuilder', inject([ShowHideService], (service: ShowHideService) => {
+    spyOn(queryBuilderMock, 'addOrUpdateFilter');
+    spyOn(queryBuilderMock, 'removeFilter');
+
+    service.setFilterFor('RESOLVE', false);
+    expect(queryBuilderMock.removeFilter).toHaveBeenCalledWith(new Filter('-alert_status', 'RESOLVE', false));
+    expect(queryBuilderMock.addOrUpdateFilter).not.toHaveBeenCalled();
+  }));
+
+  it('should be able to add DISMISS filter to QueryBuilder', inject([ShowHideService], (service: ShowHideService) => {
+    spyOn(queryBuilderMock, 'addOrUpdateFilter');
+    spyOn(queryBuilderMock, 'removeFilter');
+
+    service.setFilterFor('DISMISS', true);
+    expect(queryBuilderMock.addOrUpdateFilter).toHaveBeenCalledWith(new Filter('-alert_status', 'DISMISS', false));
+    expect(queryBuilderMock.removeFilter).not.toHaveBeenCalled();
+  }));
+
+  it('should be able to remove DISMISS filter to QueryBuilder', inject([ShowHideService], (service: ShowHideService) => {
+    spyOn(queryBuilderMock, 'addOrUpdateFilter');
+    spyOn(queryBuilderMock, 'removeFilter');
+
+    service.setFilterFor('DISMISS', false);
+    expect(queryBuilderMock.removeFilter).toHaveBeenCalledWith(new Filter('-alert_status', 'DISMISS', false));
+    expect(queryBuilderMock.addOrUpdateFilter).not.toHaveBeenCalled();
+  }));
+});
diff --git a/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide.service.ts b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide.service.ts
new file mode 100644
index 0000000..f00251a
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/alerts/configure-rows/show-hide/show-hide.service.ts
@@ -0,0 +1,70 @@
+/**
+ * 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 { Injectable } from '@angular/core';
+import { QueryBuilder } from 'app/alerts/alerts-list/query-builder';
+import { Filter } from 'app/model/filter';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ShowHideService {
+
+  private readonly FIELD = '-alert_status';
+  private readonly RESOLVE = 'RESOLVE';
+  private readonly DISMISS = 'DISMISS';
+
+  public readonly HIDE_RESOLVE_STORAGE_KEY = 'hideResolvedAlertItems';
+  public readonly HIDE_DISMISS_STORAGE_KEY = 'hideDismissAlertItems';
+
+  private readonly resolveFilter = new Filter(this.FIELD, this.RESOLVE, false);
+  private readonly dismissFilter = new Filter(this.FIELD, this.DISMISS, false);
+
+  hideResolved = false;
+  hideDismissed = false;
+
+  constructor(public queryBuilder: QueryBuilder) {
+    this.hideResolved = localStorage.getItem(this.HIDE_RESOLVE_STORAGE_KEY) === 'true';
+    this.setFilterFor(this.RESOLVE, this.hideResolved);
+
+    this.hideDismissed = localStorage.getItem(this.HIDE_DISMISS_STORAGE_KEY) === 'true';
+    this.setFilterFor(this.DISMISS, this.hideDismissed);
+  }
+
+  setFilterFor(alertStatus, isHide) {
+    const filterOperation = ((isFilterToAdd) => {
+      if (isFilterToAdd) {
+        return this.queryBuilder.addOrUpdateFilter.bind(this.queryBuilder);
+      } else {
+        return this.queryBuilder.removeFilter.bind(this.queryBuilder);
+      }
+    })(isHide);
+
+    switch (alertStatus) {
+      case this.DISMISS:
+        filterOperation(this.dismissFilter);
+        this.hideDismissed = isHide;
+        localStorage.setItem(this.HIDE_DISMISS_STORAGE_KEY, isHide);
+        break;
+      case this.RESOLVE:
+        filterOperation(this.resolveFilter);
+        this.hideResolved = isHide;
+        localStorage.setItem(this.HIDE_RESOLVE_STORAGE_KEY, isHide);
+        break;
+    }
+  }
+}
diff --git a/metron-interface/metron-alerts/src/app/app.module.ts b/metron-interface/metron-alerts/src/app/app.module.ts
index 7abc2ae..560171e 100644
--- a/metron-interface/metron-alerts/src/app/app.module.ts
+++ b/metron-interface/metron-alerts/src/app/app.module.ts
@@ -32,7 +32,6 @@ import {SaveSearchModule} from './alerts/save-search/save-search.module';
 import {SaveSearchService} from './service/save-search.service';
 import {SavedSearchesModule} from './alerts/saved-searches/saved-searches.module';
 import {ConfigureRowsModule} from './alerts/configure-rows/configure-rows.module';
-import {SwitchModule} from './shared/switch/switch.module';
 import {ColumnNamesService} from './service/column-names.service';
 import {DataSource} from './service/data-source';
 import {ElasticSearchLocalstorageImpl} from './service/elasticsearch-localstorage-impl';
@@ -73,7 +72,6 @@ export function initConfig(appConfigService: AppConfigService) {
     ConfigureRowsModule,
     SaveSearchModule,
     SavedSearchesModule,
-    SwitchModule,
     PcapModule
   ],
   providers: [{ provide: APP_INITIALIZER, useFactory: initConfig, deps: [AppConfigService], multi: true },
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 e599a88..47f211b 100644
--- a/metron-interface/metron-alerts/src/app/service/search.service.ts
+++ b/metron-interface/metron-alerts/src/app/service/search.service.ts
@@ -29,7 +29,6 @@ import {GroupResult} from '../model/group-result';
 import { RestError } from '../model/rest-error';
 import {INDEXES} from '../utils/constants';
 import {ColumnMetadata} from '../model/column-metadata';
-import {QueryBuilder} from '../alerts/alerts-list/query-builder';
 import { AppConfigService } from './app-config.service';
 
 @Injectable()
@@ -80,11 +79,11 @@ export class SearchService {
     catchError(HttpUtil.handleError));
   }
 
-  public pollSearch(queryBuilder: QueryBuilder): Observable<SearchResponse> {
+  public pollSearch(searchRequest: SearchRequest): Observable<SearchResponse> {
     return this.ngZone.runOutsideAngular(() => {
       return this.ngZone.run(() => {
         return observableInterval(this.interval * 1000).pipe(switchMap(() => {
-          return this.search(queryBuilder.searchRequest);
+          return this.search(searchRequest);
         }));
       });
     });
diff --git a/metron-interface/metron-alerts/src/app/shared/switch/switch.component.html b/metron-interface/metron-alerts/src/app/shared/switch/switch.component.html
index 794388f..14b722e 100644
--- a/metron-interface/metron-alerts/src/app/shared/switch/switch.component.html
+++ b/metron-interface/metron-alerts/src/app/shared/switch/switch.component.html
@@ -13,7 +13,7 @@
   -->
 <div>
   <label class="switch">
-    <input type="checkbox" class="inputdemo">
+    <input type="checkbox" [checked]="selected" (change)="onValueChange($event)" class="inputdemo">
     <div class="slider round"></div>
   </label>
   <label> {{ text }}</label>
diff --git a/metron-interface/metron-alerts/src/app/shared/switch/switch.component.ts b/metron-interface/metron-alerts/src/app/shared/switch/switch.component.ts
index deca9b5..6708081 100644
--- a/metron-interface/metron-alerts/src/app/shared/switch/switch.component.ts
+++ b/metron-interface/metron-alerts/src/app/shared/switch/switch.component.ts
@@ -15,19 +15,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { Component, OnInit, Input } from '@angular/core';
+import { Component, Input, Output, EventEmitter } from '@angular/core';
 
 @Component({
   selector: 'app-switch',
   templateUrl: './switch.component.html',
   styleUrls: ['./switch.component.scss']
 })
-export class SwitchComponent implements OnInit {
-  @Input() text: string;
+export class SwitchComponent {
 
-  constructor() { }
+  @Output() onChange: EventEmitter<Event> = new EventEmitter();
+  @Input() text: string;
+  @Input() selected = false;
 
-  ngOnInit() {
+  onValueChange(event) {
+    this.onChange.emit(event.target.checked);
   }
 
 }
diff --git a/metron-interface/metron-alerts/src/app/shared/switch/switch.module.ts b/metron-interface/metron-alerts/src/app/shared/switch/switch.module.ts
index f5011ad..3f688de 100644
--- a/metron-interface/metron-alerts/src/app/shared/switch/switch.module.ts
+++ b/metron-interface/metron-alerts/src/app/shared/switch/switch.module.ts
@@ -15,16 +15,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {NgModule} from '@angular/core';
+import { NgModule } from '@angular/core';
 
-import {SharedModule} from '../shared.module';
-import {SwitchComponent} from './switch.component';
+import { SharedModule } from '../shared.module';
+import { SwitchComponent } from './switch.component';
 
 @NgModule({
-  imports: [SharedModule],
-  exports: [SwitchComponent],
-  declarations: [SwitchComponent],
+  imports: [ SharedModule ],
+  exports: [ SwitchComponent ],
+  declarations: [ SwitchComponent ],
   providers: [],
 })
-export class SwitchModule {
-}
+export class SwitchModule {}