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 {}