You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@metron.apache.org by ni...@apache.org on 2017/11/27 22:44:45 UTC
[2/2] metron git commit: METRON-1252 Build UI for grouping alerts
into meta-alerts (iraghumitra via nickwallen) closes apache/metron#803
METRON-1252 Build UI for grouping alerts into meta-alerts (iraghumitra via nickwallen) closes apache/metron#803
Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/d07833a2
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/d07833a2
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/d07833a2
Branch: refs/heads/master
Commit: d07833a25470bafafdeee5e9e4054cb4a013bedb
Parents: a1408d7
Author: iraghumitra <ra...@gmail.com>
Authored: Mon Nov 27 17:14:13 2017 -0500
Committer: nickallen <ni...@apache.org>
Committed: Mon Nov 27 17:14:13 2017 -0500
----------------------------------------------------------------------
.../e2e/alert-details/alert-details.po.ts | 31 ++-
.../alert-details-status.e2e-spec.ts | 3 +-
.../e2e/alerts-list/alerts-list.e2e-spec.ts | 18 +-
.../e2e/alerts-list/alerts-list.po.ts | 63 ++++-
.../configure-table/configure-table.e2e-spec.ts | 3 +-
.../meta-alerts/meta-alert.e2e-spec.ts | 248 +++++++++++++++++++
.../alerts-list/meta-alerts/meta-alert.po.ts | 43 ++++
.../alerts-list/tree-view/tree-view.e2e-spec.ts | 95 ++++---
.../e2e/alerts-list/tree-view/tree-view.po.ts | 74 +++++-
.../metron-alerts/e2e/utils/e2e_util.ts | 12 +
.../metron-alerts/protractor.conf.js | 9 +-
.../metron-alerts/src/_variables.scss | 2 +
.../alert-details/alert-details-keys.pipe.ts | 31 +++
.../alert-details/alert-details.component.html | 34 ++-
.../alert-details/alert-details.component.scss | 33 +++
.../alert-details/alert-details.component.ts | 44 +++-
.../alert-details/alerts-details.module.ts | 6 +-
.../alerts-list/alerts-list.component.html | 10 +-
.../alerts/alerts-list/alerts-list.component.ts | 77 ++++--
.../alerts/alerts-list/alerts-list.module.ts | 3 +-
.../src/app/alerts/alerts-list/query-builder.ts | 5 +-
.../table-view/table-view.component.html | 101 +++++++-
.../table-view/table-view.component.scss | 15 +-
.../table-view/table-view.component.ts | 145 +++++++++--
.../alerts-list/tree-view/tree-group-data.ts | 1 +
.../tree-view/tree-view.component.html | 11 +-
.../tree-view/tree-view.component.scss | 17 ++
.../tree-view/tree-view.component.ts | 117 +++++++--
.../meta-alerts/meta-alerts.component.html | 52 ++++
.../meta-alerts/meta-alerts.component.scss | 58 +++++
.../alerts/meta-alerts/meta-alerts.component.ts | 79 ++++++
.../alerts/meta-alerts/meta-alerts.module.ts | 13 +
.../alerts/meta-alerts/meta-alerts.routing.ts | 24 ++
.../metron-alerts/src/app/app.module.ts | 8 +-
.../metron-alerts/src/app/model/alert-source.ts | 1 +
.../metron-alerts/src/app/model/filter.ts | 28 ++-
.../metron-alerts/src/app/model/get-request.ts | 29 +++
.../app/model/meta-alert-add-remove-request.ts | 23 ++
.../src/app/model/meta-alert-create-request.ts | 24 ++
.../src/app/model/replace-request.ts | 24 ++
.../src/app/model/search-request.ts | 2 +-
.../metron-alerts/src/app/model/sort-field.ts | 5 +
.../src/app/service/meta-alert.service.ts | 88 +++++++
.../src/app/service/update.service.ts | 20 +-
.../src/app/shared/metron-dialog-box.ts | 4 +-
.../src/app/shared/pipes/time-lapse.pipe.ts | 17 ++
.../src/app/shared/shared.module.ts | 7 +-
.../shared/time-range/time-range.component.ts | 7 +-
.../metron-alerts/src/app/utils/constants.ts | 5 +
.../metron-alerts/src/app/utils/utils.ts | 22 +-
.../src/environments/environment.e2e.ts | 4 +-
metron-interface/metron-alerts/src/styles.scss | 59 +++++
52 files changed, 1645 insertions(+), 209 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts b/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
index ed71627..18cb273 100644
--- a/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
+++ b/metron-interface/metron-alerts/e2e/alert-details/alert-details.po.ts
@@ -106,11 +106,13 @@ export class MetronAlertDetailsPage {
}
getCommentIconCountInListView() {
- return element.all(by.css('app-table-view .fa.fa-comments-o')).count();
+ let commentsElement = element.all(by.css('app-table-view .fa.fa-comments-o'));
+ return waitForElementPresence(commentsElement).then(() => commentsElement.count());
}
getCommentIconCountInTreeView() {
- return element.all(by.css('app-tree-view .fa.fa-comments-o')).count();
+ let commentsElement = element.all(by.css('app-tree-view .fa.fa-comments-o'));
+ return waitForElementPresence(commentsElement).then(() => commentsElement.count());
}
waitForTextChange(element, previousText) {
@@ -118,4 +120,29 @@ export class MetronAlertDetailsPage {
return browser.wait(EC.not(EC.textToBePresentInElement(element, previousText)));
}
+ getAlertNameOrId() {
+ let nameSelector = element(by.css('app-alert-details .editable-text'));
+ return waitForElementVisibility(nameSelector).then(() => nameSelector.getText());
+ }
+
+ clickRenameMetaAlert() {
+ element(by.css('app-alert-details .editable-text')).click();
+ }
+
+ renameMetaAlert(name: string) {
+ element(by.css('app-alert-details input.form-control')).sendKeys(name);
+ }
+
+ cancelRename() {
+ element(by.css('app-alert-details .input-group .fa.fa-times')).click();
+ }
+
+ saveRename() {
+ element(by.css('app-alert-details .fa.fa-check')).click();
+ }
+
+ getAlertDetailsCount() {
+ let titleElement = element.all(by.css('app-alert-details .alert-details-title')).get(0);
+ return waitForElementVisibility(titleElement).then(() => element.all(by.css('app-alert-details .alert-details-title')).count());
+ }
}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts b/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
index 4e25f82..d051a78 100644
--- a/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
+++ b/metron-interface/metron-alerts/e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts
@@ -50,9 +50,10 @@ describe('metron-alerts alert status', function() {
it('should change alert statuses', () => {
let alertId = 'c4c5e418-3938-099e-bb0d-37028a98dca8';
-
+
page.navigateTo(alertId);
page.clickNew();
+ expect(page.getAlertStatus('ANY')).toEqual('NEW');
page.clickOpen();
expect(page.getAlertStatus('NEW')).toEqual('OPEN');
expect(listPage.getAlertStatusById(alertId)).toEqual('OPEN');
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
index b0574ee..358878d 100644
--- a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
+++ b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.e2e-spec.ts
@@ -24,8 +24,8 @@ import { loadTestData, deleteTestData } from '../utils/e2e_util';
describe('metron-alerts App', function() {
let page: MetronAlertsPage;
let loginPage: LoginPage;
- let columnNames = [ 'Score', 'id', 'timestamp', 'source:type', 'ip_src_addr', 'enrichm...:country',
- 'ip_dst_addr', 'host', 'alert_status', '', ''];
+ let columnNames = [ '', 'Score', 'id', 'timestamp', 'source:type', 'ip_src_addr', 'enrichm...:country',
+ 'ip_dst_addr', 'host', 'alert_status', '', '', ''];
let colNamesColumnConfig = [ 'score', 'id', 'timestamp', 'source:type', 'ip_src_addr', 'enrichments:geo:ip_dst_addr:country',
'ip_dst_addr', 'host', 'alert_status' ];
@@ -58,8 +58,9 @@ describe('metron-alerts App', function() {
expect(page.isPausePlayRefreshButtonPresent()).toEqualBcoz(true, 'for pause/play button');
expect(page.isConfigureTableColumnsPresent()).toEqualBcoz(true, 'for alerts table column configure button');
- expect(page.getAlertTableTitle()).toEqualBcoz('Alerts (169)', 'for alerts title');
- expect(page.getActionDropdownItems()).toEqualBcoz([ 'Open', 'Dismiss', 'Escalate', 'Resolve' ], 'for default dropdown actions');
+ expect(page.getChangesAlertTableTitle('Alerts (0)')).toEqualBcoz('Alerts (169)', 'for alerts title');
+ expect(page.getActionDropdownItems()).toEqualBcoz([ 'Open', 'Dismiss', 'Escalate', 'Resolve', 'Add to Alert' ],
+ 'for default dropdown actions');
expect(page.getTableColumnNames()).toEqualBcoz(columnNames, 'for default column names for alert list table');
});
@@ -300,6 +301,9 @@ describe('metron-alerts App', function() {
});
it('should have all time-range included while searching', () => {
+ let startDate = new Date(1505325575000);
+ let endDate = new Date(1505325580000);
+
page.clearLocalStorage();
page.clickDateSettings();
@@ -309,8 +313,10 @@ describe('metron-alerts App', function() {
/* Select custom date for time range */
page.clickDateSettings();
- page.setDate(0, '2017', 'September', '13', '23', '29', '35');
- page.setDate(1, '2017', 'September', '13', '23', '29', '40');
+ page.setDate(0, String(startDate.getFullYear()), startDate.toLocaleString('en-us', { month: "long" }), String(startDate.getDate()),
+ String(startDate.getHours()), String(startDate.getMinutes()), String(startDate.getSeconds()));
+ page.setDate(1, String(endDate.getFullYear()), endDate.toLocaleString('en-us', { month: "long" }), String(endDate.getDate()),
+ String(endDate.getHours()), String(endDate.getMinutes()), String(endDate.getSeconds()));
page.selectTimeRangeApplyButton();
expect(page.getChangesAlertTableTitle('Alerts (169)')).toEqual('Alerts (5)');
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
index 4a97917..45f4fee 100644
--- a/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
+++ b/metron-interface/metron-alerts/e2e/alerts-list/alerts-list.po.ts
@@ -17,6 +17,7 @@
*/
import {browser, element, by, protractor} from 'protractor';
+import * as moment from 'moment/moment';
import {waitForElementVisibility, waitForElementPresence, waitForElementInVisibility} from '../utils/e2e_util';
export class MetronAlertsPage {
@@ -172,7 +173,7 @@ export class MetronAlertsPage {
}
clickTableText(name: string) {
- waitForElementPresence(element.all(by.css('app-table-view tbody tr a'))).then(() => element.all(by.linkText(name)).get(0).click());
+ waitForElementPresence(element.all(by.linkText(name))).then(() => element.all(by.linkText(name)).get(0).click());
}
clickClearSearch() {
@@ -278,14 +279,20 @@ export class MetronAlertsPage {
});
}
- getAlertStatus(rowIndex: number, previousText) {
+ getAlertStatus(rowIndex: number, previousText: string, colIndex = 8) {
let row = element.all(by.css('app-alerts-list tbody tr')).get(rowIndex);
- let column = row.all(by.css('td a')).get(8);
+ let column = row.all(by.css('td a')).get(colIndex);
return this.waitForTextChange(column, previousText).then(() => {
return column.getText();
});
}
+ waitForMetaAlert() {
+ browser.sleep(2000);
+ return element(by.css('button[data-name="search"]')).click()
+ .then(() => waitForElementPresence(element(by.css('.icon-cell.dropdown-cell'))));
+ }
+
isDateSeettingDisabled() {
return element.all(by.css('app-time-range button.btn.btn-search[disabled=""]')).count().then((count) => { return (count === 1); });
}
@@ -298,7 +305,7 @@ export class MetronAlertsPage {
getTimeRangeTitles() {
return element.all(by.css('app-time-range .title')).getText();
}
-
+
getQuickTimeRanges() {
return element.all(by.css('app-time-range .quick-ranges span')).getText();
}
@@ -315,7 +322,7 @@ export class MetronAlertsPage {
element.all(by.cssContainingText('.quick-ranges span', quickRange)).get(0).click();
browser.sleep(2000);
}
-
+
getTimeRangeButtonText() {
return element.all(by.css('app-time-range button.btn-search span')).get(0).getText();
}
@@ -351,10 +358,44 @@ export class MetronAlertsPage {
}
getAlertStatusById(id: string) {
- return element(by.css('a[title="' + id +'"]'))
+ return element(by.css('a[title="' + id + '"]'))
.element(by.xpath('../..')).all(by.css('td a')).get(8).getText();
}
+ sortTable(colName: string) {
+ element.all(by.css('table thead th')).all(by.linkText(colName)).get(0).click();
+ }
+
+ getCellValue(rowIndex: number, colIndex: number, previousText: string) {
+ let cellElement = element.all(by.css('table tbody tr')).get(rowIndex).all(by.css('td')).get(colIndex);
+ return this.waitForTextChange(cellElement, previousText).then(() => cellElement.getText());
+ }
+
+ expandMetaAlert(rowIndex: number) {
+ element.all(by.css('table tbody tr')).get(rowIndex).element(by.css('.icon-cell.dropdown-cell')).click();
+ }
+
+ getHiddenRowCount() {
+ return element.all(by.css('table tbody tr.d-none')).count();
+ }
+
+ getNonHiddenRowCount() {
+ return element.all(by.css('table tbody tr:not(.d-none)')).count();
+ }
+
+ getAllRowsCount() {
+ return element.all(by.css('table tbody tr')).count();
+ }
+
+ clickOnMetaAlertRow(rowIndex: number) {
+ element.all(by.css('table tbody tr')).get(rowIndex).all(by.css('td')).get(5).click();
+ browser.sleep(2000);
+ }
+
+ removeAlert(rowIndex: number) {
+ return element.all(by.css('app-table-view .fa-chain-broken')).get(rowIndex).click();
+ }
+
loadSavedSearch(name: string) {
element.all(by.css('app-saved-searches metron-collapse')).get(1).element(by.css('li[title="'+ name +'"]')).click();
browser.sleep(1000);
@@ -376,18 +417,22 @@ export class MetronAlertsPage {
let retArr = [arr[0]];
for (let i=1; i < arr.length; i++) {
let dateStr = arr[i].split(' to ');
- let fromTime = new Date(dateStr[0]).getTime();
- let toTime = new Date(dateStr[1]).getTime();
+ let fromTime = moment.utc(dateStr[0], 'YYYY-MM-DD HH:mm:ss Z').unix() * 1000;
+ let toTime = moment.utc(dateStr[1], 'YYYY-MM-DD HH:mm:ss Z').unix() * 1000;
retArr.push((toTime - fromTime) + '');
}
return retArr;
});
}
-
+
renameColumn(name: string, value: string) {
element(by.cssContainingText('app-configure-table span', name))
.element(by.xpath('../..'))
.element(by.css('.input')).sendKeys(value);
}
+ getTableCellValues(cellIndex: number, startRowIndex: number, endRowIndex: number): any {
+ return element.all(by.css('table tbody tr td:nth-child(' + cellIndex + ')')).getText()
+ .then(val => val.slice(startRowIndex, endRowIndex));
+ }
}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts b/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
index ddad558..6741e2f 100644
--- a/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
+++ b/metron-interface/metron-alerts/e2e/alerts-list/configure-table/configure-table.e2e-spec.ts
@@ -76,8 +76,7 @@ describe('metron-alerts configure table', function() {
expect(page.getChangesAlertTableTitle('Alerts (169)')).toEqual('Alerts (25)');
page.clickClearSearch();
- let columnNames = ['Score','id', 'timestamp','source:type','ip_src_addr','Country','ip_dst_addr','host','alert_status','',''];
- expect(page.getTableColumnNames()).toEqualBcoz(columnNames, 'for renamed column names for alert list table');
+ expect(page.getTableColumnNames()).toContain('Country', 'for renamed column names for alert list table');
});
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.e2e-spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.e2e-spec.ts b/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.e2e-spec.ts
new file mode 100644
index 0000000..38bacee
--- /dev/null
+++ b/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.e2e-spec.ts
@@ -0,0 +1,248 @@
+/// <reference path="../../matchers/custom-matchers.d.ts"/>
+/**
+ * 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 { MetronAlertsPage } from '../alerts-list.po';
+import {customMatchers} from '../../matchers/custom-matchers';
+import {LoginPage} from '../../login/login.po';
+import {loadTestData, deleteTestData} from '../../utils/e2e_util';
+import {TreeViewPage} from '../tree-view/tree-view.po';
+import {MetronAlertDetailsPage} from '../../alert-details/alert-details.po';
+import {MetaAlertPage} from './meta-alert.po';
+import {AlertFacetsPage} from '../alert-filters/alert-filters.po';
+
+describe('meta-alerts workflow', function() {
+ let detailsPage: MetronAlertDetailsPage;
+ let tablePage: MetronAlertsPage;
+ let metaAlertPage: MetaAlertPage;
+ let treePage: TreeViewPage;
+ let loginPage: LoginPage;
+ let alertFacetsPage: AlertFacetsPage;
+
+ beforeAll(() => {
+ loadTestData();
+
+ loginPage = new LoginPage();
+ loginPage.login();
+ tablePage = new MetronAlertsPage();
+ treePage = new TreeViewPage();
+ tablePage = new MetronAlertsPage();
+ metaAlertPage = new MetaAlertPage();
+ detailsPage = new MetronAlertDetailsPage();
+ alertFacetsPage = new AlertFacetsPage();
+ });
+
+ afterAll(() => {
+ loginPage.logout();
+ deleteTestData();
+ });
+
+ beforeEach(() => {
+ jasmine.addMatchers(customMatchers);
+ });
+
+ it('should have all the steps for meta alerts workflow', () => {
+ let comment1 = 'This is a sample comment';
+ let userNameAndTimestamp = '- admin - a few seconds ago';
+ let confirmText = 'Do you wish to create a meta alert with 113 selected alerts?';
+ let dashRowValues = {
+ 'firstDashRow': ['0', '192.168.138.158', 'ALERTS', '113'],
+ 'secondDashRow': ['0', '192.168.66.1', 'ALERTS', '56']
+ };
+
+ tablePage.navigateTo();
+
+ /* Create Meta Alert */
+ treePage.selectGroup('ip_src_addr');
+ expect(treePage.getDashGroupValues('192.168.138.158')).toEqualBcoz(dashRowValues.firstDashRow, 'First Dashrow to be present');
+ expect(treePage.getDashGroupValues('192.168.66.1')).toEqualBcoz(dashRowValues.secondDashRow, 'Second Dashrow to be present');
+
+ treePage.clickOnMergeAlerts('192.168.138.158');
+ expect(treePage.getConfirmationText()).toEqualBcoz(confirmText, 'confirmation text to be present');
+ treePage.clickNoForConfirmation();
+
+ treePage.clickOnMergeAlerts('192.168.138.158');
+ treePage.clickYesForConfirmation();
+
+ treePage.waitForElementToDisappear('192.168.138.158');
+
+ treePage.unGroup();
+
+ /* Table should have all alerts */
+ tablePage.waitForMetaAlert();
+ expect(tablePage.getPaginationText()).toEqualBcoz('1 - 25 of 57', 'pagination text to be present');
+ expect(tablePage.getCellValue(0, 2, '(114)')).toContain('(113)', 'number of alerts in a meta alert should be correct');
+ expect(tablePage.getNonHiddenRowCount()).toEqualBcoz(25, '25 rows to be visible');
+ expect(tablePage.getAllRowsCount()).toEqualBcoz(138, '138 rows to be available');
+ expect(tablePage.getHiddenRowCount()).toEqualBcoz(113, '113 rows to be hidden');
+ tablePage.expandMetaAlert(0);
+ expect(tablePage.getNonHiddenRowCount()).toEqualBcoz(138, '138 rows to be visible after group expand');
+ expect(tablePage.getAllRowsCount()).toEqualBcoz(138, '138 rows to be available after group expand');
+ expect(tablePage.getHiddenRowCount()).toEqualBcoz(0, '0 rows to be hidden after group expand');
+
+ /* Meta Alert Status Change */
+ tablePage.toggleAlertInList(0);
+ tablePage.clickActionDropdownOption('Open');
+ expect(tablePage.getAlertStatus(0, 'NEW', 2)).toEqual('OPEN');
+ expect(tablePage.getAlertStatus(1, 'NEW')).toEqual('OPEN');
+ expect(tablePage.getAlertStatus(2, 'NEW')).toEqual('OPEN');
+
+ /* Details Edit Should work - add comments - remove comments - multiple alerts */
+ tablePage.clickOnMetaAlertRow(0);
+ expect(detailsPage.getAlertDetailsCount()).toEqualBcoz(113, '113 alert details should be present');
+ detailsPage.clickRenameMetaAlert();
+ detailsPage.renameMetaAlert('e2e-meta-alert');
+ detailsPage.cancelRename();
+ expect(detailsPage.getAlertNameOrId()).not.toEqual('e2e-meta-alert');
+ detailsPage.clickRenameMetaAlert();
+ detailsPage.renameMetaAlert('e2e-meta-alert');
+ detailsPage.saveRename();
+ expect(detailsPage.getAlertNameOrId()).toEqual('e2e-meta-alert');
+
+ detailsPage.clickCommentsInSideNav();
+ detailsPage.addCommentAndSave(comment1, 0);
+ expect(detailsPage.getCommentsText()).toEqual([comment1]);
+ expect(detailsPage.getCommentsUserNameAndTimeStamp()).toEqual([userNameAndTimestamp]);
+ expect(detailsPage.getCommentIconCountInListView()).toEqual(1);
+
+ detailsPage.deleteComment();
+ detailsPage.clickYesForConfirmation();
+
+ detailsPage.closeDetailPane();
+
+ /* Add to alert */
+ tablePage.toggleAlertInList(3);
+ tablePage.clickActionDropdownOption('Add to Alert');
+ expect(metaAlertPage.getPageTitle()).toEqualBcoz('Add to Alert', 'Add Alert Title should be present');
+ expect(metaAlertPage.getMetaAlertsTitle()).toEqualBcoz('SELECT OPEN ALERT', 'select open alert title should be present');
+ expect(metaAlertPage.getAvailableMetaAlerts()).toEqualBcoz('e2e-meta-alert (113)', 'Meta alert should be present');
+ metaAlertPage.selectRadio();
+ metaAlertPage.addToMetaAlert();
+ expect(tablePage.getCellValue(0, 2, '(113')).toContain('(114)', 'alert count should be incremented');
+
+ /* Remove from alert */
+ let removAlertConfirmText = 'Do you wish to remove the alert from the meta alert?';
+ tablePage.removeAlert(2);
+ expect(treePage.getConfirmationText()).toEqualBcoz(removAlertConfirmText, 'confirmation text to remove alert from meta alert');
+ treePage.clickYesForConfirmation();
+ expect(tablePage.getCellValue(0, 2, '(114')).toContain('(113)', 'alert count should be decremented');
+
+ /* Delete Meta Alert */
+ let removeMetaAlertConfirmText = 'Do you wish to remove all the alerts from meta alert?';
+ tablePage.removeAlert(0);
+ expect(treePage.getConfirmationText()).toEqualBcoz(removeMetaAlertConfirmText, 'confirmation text to remove meta alert');
+ treePage.clickYesForConfirmation();
+ });
+
+ it('should create a meta alert from nesting of more than one level', () => {
+ let groupByItems = {
+ 'source:type': '1',
+ 'ip_dst_addr': '7',
+ 'host': '9',
+ 'enrichm...:country': '3',
+ 'ip_src_addr': '2'
+ };
+ let alertsInMetaAlerts = [
+ '82f8046d-d...03b17480dd',
+ '5c1825f6-7...da3abe3aec',
+ '9041285e-9...a04a885b53',
+ 'ed906df7-2...91cc54c2f3',
+ 'c894bbcf-3...74cf0cc1fe',
+ 'e63ff7ae-d...cddbe0c0b3',
+ '3c346bf9-b...cb04b43210',
+ 'dcc483af-c...7bb802b652',
+ 'b71f085d-6...a4904d8fcf',
+ '754b4f63-3...b39678207f',
+ 'd9430af3-e...9a18600ab2',
+ '9a943c94-c...3b9046b782',
+ 'f39dc401-3...1f9cf02cd9',
+ 'd887fe69-c...2fdba06dbc',
+ 'e38be207-b...60a43e3378',
+ 'eba8eccb-b...0005325a90',
+ 'adca96e3-1...979bf0b5f1',
+ '42f4ce28-8...b3d575b507',
+ 'aed3d10f-b...8b8a139f25',
+ 'a5e95569-a...0e2613b29a'
+ ];
+
+ let alertsAfterDeletedInMetaAlerts = [
+ '82f8046d-d...03b17480dd',
+ '5c1825f6-7...da3abe3aec',
+ '9041285e-9...a04a885b53',
+ 'ed906df7-2...91cc54c2f3',
+ 'e63ff7ae-d...cddbe0c0b3',
+ '3c346bf9-b...cb04b43210',
+ 'dcc483af-c...7bb802b652',
+ 'b71f085d-6...a4904d8fcf',
+ '754b4f63-3...b39678207f',
+ 'd9430af3-e...9a18600ab2',
+ '9a943c94-c...3b9046b782',
+ 'f39dc401-3...1f9cf02cd9',
+ 'd887fe69-c...2fdba06dbc',
+ 'e38be207-b...60a43e3378',
+ 'eba8eccb-b...0005325a90',
+ 'adca96e3-1...979bf0b5f1',
+ '42f4ce28-8...b3d575b507',
+ 'aed3d10f-b...8b8a139f25',
+ 'a5e95569-a...0e2613b29a'
+ ];
+
+ // Create a meta alert from a group that is nested by more than 1 level
+ treePage.selectGroup('source:type');
+ treePage.selectGroup('ip_dst_addr');
+ treePage.expandDashGroup('alerts_ui_e2e');
+
+ treePage.clickOnMergeAlertsInTable('alerts_ui_e2e', '224.0.0.251', 0);
+ treePage.clickYesForConfirmation();
+
+ treePage.unGroup();
+ tablePage.waitForMetaAlert();
+
+ expect(tablePage.getPaginationText()).toEqualBcoz('1 - 25 of 150', 'pagination text to be present');
+
+ // Meta Alert should appear in Filters
+ alertFacetsPage.toggleFacetState(4);
+ expect(alertFacetsPage.getFacetValues(4)).toEqual({'metaalert': '1' }, 'for source:type facet');
+
+ // Meta Alert should not appear in Groups
+ expect(treePage.getGroupByItemNames()).toEqualBcoz(Object.keys(groupByItems), 'Group By Elements names should be present');
+ expect(treePage.getGroupByItemCounts()).toEqualBcoz(Object.keys(groupByItems).map(key => groupByItems[key]),
+ '5 Group By Elements values should be present');
+
+
+ tablePage.setSearchText('guid:c894bbcf-3195-0708-aebe-0574cf0cc1fe');
+ expect(tablePage.getChangesAlertTableTitle('Alerts (150)')).toEqual('Alerts (1)');
+ tablePage.expandMetaAlert(0);
+ expect(tablePage.getAllRowsCount()).toEqual(21);
+ tablePage.expandMetaAlert(0);
+ tablePage.clickClearSearch();
+ expect(tablePage.getChangesAlertTableTitle('Alerts (1)')).toEqual('Alerts (150)');
+
+ // Delete a meta alert from the middle and check the data
+ tablePage.expandMetaAlert(0);
+ expect(tablePage.getTableCellValues(3, 1, 21)).toEqual(alertsInMetaAlerts);
+ tablePage.removeAlert(5);
+ treePage.clickYesForConfirmation();
+ expect(tablePage.getCellValue(0, 2, '(20')).toContain('(19)', 'alert count should be decremented');
+ expect(tablePage.getTableCellValues(3, 1, 20)).toEqual(alertsAfterDeletedInMetaAlerts);
+
+ //Remove the meta alert
+ tablePage.removeAlert(0);
+ treePage.clickYesForConfirmation();
+ });
+
+});
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.po.ts b/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.po.ts
new file mode 100644
index 0000000..762deb8
--- /dev/null
+++ b/metron-interface/metron-alerts/e2e/alerts-list/meta-alerts/meta-alert.po.ts
@@ -0,0 +1,43 @@
+/**
+ * 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 {browser, element, by} from 'protractor';
+
+export class MetaAlertPage {
+
+ getPageTitle() {
+ return element(by.css('app-meta-alerts .form-title')).getText();
+ }
+
+ getMetaAlertsTitle() {
+ return element(by.css('app-meta-alerts .title')).getText();
+ }
+
+ getAvailableMetaAlerts() {
+ return element(by.css('app-meta-alerts .guid-name-container div')).getText();
+ }
+
+ selectRadio() {
+ return element.all(by.css('app-meta-alerts .checkmark')).click();
+ }
+
+ addToMetaAlert() {
+ element.all(by.css('app-meta-alerts')).get(0).element(by.buttonText('ADD')).click();
+ browser.sleep(2000);
+ }
+}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.e2e-spec.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.e2e-spec.ts b/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.e2e-spec.ts
index caa3754..87636cd 100644
--- a/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.e2e-spec.ts
+++ b/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.e2e-spec.ts
@@ -135,41 +135,40 @@ describe('metron-alerts tree view', function () {
it('should have group details for multiple group by', () => {
- let dashRow_runLoveUs = {
- 'dashRow': ['0', 'runlove.us', 'ALERTS', '13'],
- 'firstSubGroup': '0 US (13)',
- 'firstSubGroupIdCol': ['9a969c64-b...001cb011a3', 'a651f7c3-1...a97d4966c9', 'afc36901-3...d931231ab2',
- 'd860ac35-1...f9e282d571', '04a5c3d0-9...af17c06fbc']
- };
-
- let dashRow_62_75_195_236 = {
- 'dashRow': ['0', '62.75.195.236', 'ALERTS', '18'],
- 'firstSubGroup': '0 FR (18)',
- 'firstSubGroupIdCol': ['07b29c29-9...ff19eaa888', '7cd91565-1...de5be54a6e', 'ca5bde58-a...f3a88d2df4',
- '5d6faf83-8...b88a407647', 'e2883424-f...79bb8b0606']
- };
+ let usGroupIds = ['9a969c64-b...001cb011a3','a651f7c3-1...a97d4966c9','afc36901-3...d931231ab2','d860ac35-1...f9e282d571','04a5c3d0-9...af17c06fbc'];
+ let frGroupIds = ['07b29c29-9...ff19eaa888','7cd91565-1...de5be54a6e','ca5bde58-a...f3a88d2df4','5d6faf83-8...b88a407647','e2883424-f...79bb8b0606'];
- page.selectGroup('host');
+ page.selectGroup('source:type');
+ page.selectGroup('ip_dst_addr');
page.selectGroup('enrichments:geo:ip_dst_addr:country');
- expect(page.getActiveGroups()).toEqualBcoz(['host', 'enrichments:geo:ip_dst_addr:country'], 'two groups should be selected');
-
- expect(page.getDashGroupValues('runlove.us')).toEqualBcoz(dashRow_runLoveUs.dashRow,
- 'Dash Group Values should be present for runlove.us');
- page.expandDashGroup('runlove.us');
- expect(page.getSubGroupValues('runlove.us', 'US')).toEqualBcoz(dashRow_runLoveUs.firstSubGroup,
- 'Dash Group Values should be present for runlove.us');
- page.expandSubGroup('runlove.us', 'US');
- expect(page.getCellValuesFromTable('runlove.us', 'id', '04a5c3d0-9...af17c06fbc')).toEqual(dashRow_runLoveUs.firstSubGroupIdCol,
- 'id should not be sorted');
-
- expect(page.getDashGroupValues('62.75.195.236')).toEqualBcoz(dashRow_62_75_195_236.dashRow, 'Dash Group Values should be present');
- page.expandDashGroup('62.75.195.236');
- expect(page.getSubGroupValues('62.75.195.236', 'FR')).toEqualBcoz(dashRow_62_75_195_236.firstSubGroup,
- 'Dash Group Values should be present for 62.75.195.236');
- page.expandSubGroup('62.75.195.236', 'FR');
- expect(page.getCellValuesFromTable('62.75.195.236', 'id', 'e2883424-f...79bb8b0606')).toEqual(dashRow_62_75_195_236.firstSubGroupIdCol,
- 'id should not be sorted');
+ expect(page.getActiveGroups()).toEqualBcoz(['source:type', 'ip_dst_addr', 'enrichments:geo:ip_dst_addr:country'], '3 groups should be selected');
+
+ expect(page.getDashGroupValues('alerts_ui_e2e')).toEqualBcoz(['0', 'alerts_ui_e2e', 'ALERTS', '169'],
+ 'Top Level Group Values should be present for alerts_ui_e2e');
+
+ page.expandDashGroup('alerts_ui_e2e');
+ expect(page.getSubGroupValuesByPosition('alerts_ui_e2e', '204.152.254.221', 0)).toEqualBcoz('0 204.152.254.221 (13)',
+ 'Second Level Group Values should be present for 204.152.254.221');
+ page.expandSubGroupByPosition('alerts_ui_e2e', '204.152.254.221', 0);
+ expect(page.getSubGroupValuesByPosition('alerts_ui_e2e', 'US', 0)).toEqualBcoz('0 US (13)',
+ 'Third Level Group Values should be present for US');
+
+ page.expandSubGroup('alerts_ui_e2e', 'US');
+ expect(page.getSubGroupValuesByPosition('alerts_ui_e2e', 'US', 0)).toEqualBcoz('0 US (13)',
+ 'Third Level Group Values should not change when expanded for US');
+ expect(page.getCellValuesFromTable('alerts_ui_e2e', 'id', '04a5c3d0-9...af17c06fbc')).toEqual(usGroupIds, 'rows should be present for US');
+
+
+ page.expandSubGroup('alerts_ui_e2e', '62.75.195.236');
+ expect(page.getSubGroupValuesByPosition('alerts_ui_e2e', 'FR', 1)).toEqualBcoz('0 FR (23)',
+ 'Third Level Group Values should be present for FR');
+
+ page.expandSubGroupByPosition('alerts_ui_e2e', 'FR', 1);
+ expect(page.getSubGroupValuesByPosition('alerts_ui_e2e', 'FR', 1)).toEqualBcoz('0 FR (23)',
+ 'Third Level Group Values should not change when expanded for FR');
+ expect(page.getCellValuesFromTable('alerts_ui_e2e', 'id', 'e2883424-f...79bb8b0606')).toEqual(usGroupIds.concat(frGroupIds), 'rows should be present for FR');
+
page.unGroup();
expect(page.getActiveGroups()).toEqualBcoz([], 'no groups should be selected');
});
@@ -177,19 +176,13 @@ describe('metron-alerts tree view', function () {
it('should have sort working for group details for multiple sub groups', () => {
- let usIDCol = ['dcda4423-7...0962fafc47', '9a969c64-b...001cb011a3', 'a651f7c3-1...a97d4966c9',
- 'afc36901-3...d931231ab2', 'd860ac35-1...f9e282d571'];
- let ruIDCol = ['350c0e9f-a...3cbe5b29d2', '9b47e24a-e...2ca6627943', '4cac5e2c-3...3deb1ebcc6',
- 'eb54c3fa-c...e02719c3b0', 'cace11d0-c...b1bd7b9499'];
- let frIDCol = ['07b29c29-9...ff19eaa888', '7cd91565-1...de5be54a6e', 'ca5bde58-a...f3a88d2df4',
- '5d6faf83-8...b88a407647', 'e2883424-f...79bb8b0606'];
+ let usTSCol = ['2017-09-13 17:59:32', '2017-09-13 17:59:42', '2017-09-13 17:59:53', '2017-09-13 18:00:02', '2017-09-13 18:00:14'];
+ let ruTSCol = ['2017-09-13 17:59:33', '2017-09-13 17:59:48', '2017-09-13 17:59:51', '2017-09-13 17:59:54', '2017-09-13 17:59:57'];
+ let frTSCol = ['2017-09-13 17:59:37', '2017-09-13 17:59:46', '2017-09-13 18:00:31', '2017-09-13 18:00:33', '2017-09-13 18:00:37'];
- let usSortedIDCol = ['04a5c3d0-9...af17c06fbc', '06e70f55-4...f486927126', '105529cb-2...61b58237cc',
- '4c732cb0-0...6a93129aba', '500eb5e2-6...37b0f98772'];
- let ruSortedIDCol = ['001b5451-6...38ec4221ee', '00814048-d...c9e6f27800', '0454b31e-e...0a711a36e7',
- '09552ace-9...e146579030', '0e99ba49-4...456c107bc9'];
- let frSortedIDCol = ['07b29c29-9...ff19eaa888', '2681ed49-b...c33a80d429', '29ffaeb4-e...36822e5f81',
- '2cc174d7-c...8073777309', '436b9ecf-b...5f1ece4c4d'];
+ let usSortedTSCol = ['2017-09-13 18:02:19', '2017-09-13 18:02:16', '2017-09-13 18:02:09', '2017-09-13 18:01:58', '2017-09-13 18:01:52'];
+ let ruSortedTSCol = ['2017-09-14 06:29:40', '2017-09-14 06:29:40', '2017-09-14 06:29:40', '2017-09-14 06:29:40', '2017-09-13 18:02:13'];
+ let frSortedTSCol = ['2017-09-14 06:29:40', '2017-09-14 04:29:40', '2017-09-13 18:02:20', '2017-09-13 18:02:05', '2017-09-13 18:02:04'];
page.selectGroup('source:type');
page.selectGroup('enrichments:geo:ip_dst_addr:country');
@@ -199,14 +192,18 @@ describe('metron-alerts tree view', function () {
page.expandSubGroup('alerts_ui_e2e', 'RU');
page.expandSubGroup('alerts_ui_e2e', 'FR');
- let unsortedIds = [...usIDCol, ...ruIDCol, ...frIDCol];
- let sortedIds = [...usSortedIDCol, ...ruSortedIDCol, ...frSortedIDCol];
+ let unsortedTS = [...usTSCol, ...ruTSCol, ...frTSCol];
+ let sortedTS = [...usSortedTSCol, ...ruSortedTSCol, ...frSortedTSCol];
+
+ page.sortSubGroup('alerts_ui_e2e', 'timestamp');
- expect(page.getCellValuesFromTable('alerts_ui_e2e', 'id', 'e2883424-f...79bb8b0606')).toEqual(unsortedIds, 'id should not be sorted');
+ expect(page.getCellValuesFromTable('alerts_ui_e2e', 'timestamp', '2017-09-13 18:00:37')).toEqual(unsortedTS,
+ 'timestamp should be sorted asc');
- page.sortSubGroup('alerts_ui_e2e', 'id');
+ page.sortSubGroup('alerts_ui_e2e', 'timestamp');
- expect(page.getCellValuesFromTable('alerts_ui_e2e', 'id', '436b9ecf-b...5f1ece4c4d')).toEqual(sortedIds, 'id should be sorted');
+ expect(page.getCellValuesFromTable('alerts_ui_e2e', 'timestamp', '2017-09-13 18:02:04')).toEqual(sortedTS,
+ 'timestamp should be sorted dsc');
page.unGroup();
expect(page.getActiveGroups()).toEqualBcoz([], 'no groups should be selected');
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.po.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.po.ts b/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.po.ts
index b8472df..368f709 100644
--- a/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.po.ts
+++ b/metron-interface/metron-alerts/e2e/alerts-list/tree-view/tree-view.po.ts
@@ -16,8 +16,11 @@
* limitations under the License.
*/
-import {browser, element, by, protractor} from 'protractor';
-import {waitForElementPresence, waitForTextChange} from '../../utils/e2e_util';
+import {browser, element, by} from 'protractor';
+import {
+ waitForElementPresence, waitForTextChange, waitForElementVisibility,
+ waitForElementInVisibility
+} from '../../utils/e2e_util';
export class TreeViewPage {
navigateToAlertsList() {
@@ -52,7 +55,11 @@ export class TreeViewPage {
}
getSubGroupValues(name: string, rowName: string) {
- return element(by.css('[data-name="' + name + '"] table tbody tr[data-name="' + rowName + '"]')).getText();
+ return this.getSubGroupValuesByPosition(name, rowName, 0);
+ }
+
+ getSubGroupValuesByPosition(name: string, rowName: string, position: number) {
+ return element.all(by.css('[data-name="' + name + '"] table tbody tr[data-name="' + rowName + '"]')).get(position).getText();
}
selectGroup(name: string) {
@@ -73,16 +80,25 @@ export class TreeViewPage {
}
expandDashGroup(name: string) {
- waitForElementPresence( element(by.css('[data-name="' + name + '"] .card-header'))).then(() => {
- this.scrollToDashRow(name);
- element(by.css('[data-name="' + name + '"] .card-header i')).click();
- browser.sleep(2000);
- });
+ let cardElement = element(by.css('.card[data-name="' + name +'"]'));
+ let downArrowElement = element(by.css('.card[data-name="' + name + '"] .mrow.top-group'));
+
+ return waitForElementVisibility(cardElement)
+ .then(() => browser.actions().mouseMove(cardElement).perform())
+ .then(() => waitForElementVisibility(downArrowElement))
+ .then(() => downArrowElement.click())
+ .then(() => browser.sleep(2000));
}
expandSubGroup(groupName: string, rowName: string) {
- browser.actions().mouseMove(element(by.css('[data-name="' + groupName + '"] tr[data-name="' + rowName + '"]'))).perform();
- return element(by.css('[data-name="' + groupName + '"] tr[data-name="' + rowName + '"]')).click();
+ return this.expandSubGroupByPosition(groupName, rowName, 0);
+ }
+
+ expandSubGroupByPosition(groupName: string, rowName: string, position: number) {
+ let subGroupElement = element.all(by.css('[data-name="' + groupName + '"] tr[data-name="' + rowName + '"]')).get(position);
+ return waitForElementVisibility(subGroupElement)
+ .then(() => browser.actions().mouseMove(subGroupElement).perform())
+ .then(() => subGroupElement.click());
}
getDashGroupTableValuesForRow(name: string, rowId: number) {
@@ -130,7 +146,7 @@ export class TreeViewPage {
}
getCellValuesFromTable(groupName: string, cellName: string, waitForAnchor: string) {
- return waitForElementPresence(element(by. cssContainingText('[data-name="' + cellName + '"] a', waitForAnchor))).then(() => {
+ return waitForElementPresence(element(by.cssContainingText('[data-name="' + cellName + '"] a', waitForAnchor))).then(() => {
return element.all(by.css('[data-name="' + groupName + '"] table tbody [data-name="' + cellName + '"]')).map(element => {
browser.actions().mouseMove(element).perform();
return (element.getText());
@@ -159,4 +175,40 @@ export class TreeViewPage {
return column.getText();
});
}
+
+ clickOnMergeAlerts(groupName: string) {
+ return element(by.css('[data-name="' + groupName + '"] .fa-link')).click();
+ }
+
+ clickOnMergeAlertsInTable(groupName: string, waitForAnchor: string, rowIndex: number) {
+ let elementFinder = element.all(by.css('[data-name="' + groupName + '"] table tbody tr')).get(rowIndex).element(by.css('.fa-link'));
+ return waitForElementVisibility(elementFinder)
+ .then(() => elementFinder.click());
+ }
+
+ getConfirmationText() {
+ let maskElement = element(by.className('modal-backdrop'));
+ return waitForElementVisibility(maskElement)
+ .then(() => element(by.css('.metron-dialog .modal-body')).getText());
+ }
+
+ clickNoForConfirmation() {
+ let maskElement = element(by.className('modal-backdrop'));
+ let closeButton = element(by.css('.metron-dialog')).element(by.buttonText('Cancel'));
+ waitForElementVisibility(maskElement)
+ .then(() => closeButton.click())
+ .then(() => waitForElementInVisibility(maskElement));
+ }
+
+ clickYesForConfirmation() {
+ let okButton = element(by.css('.metron-dialog')).element(by.buttonText('OK'));
+ let maskElement = element(by.className('modal-backdrop'));
+ waitForElementVisibility(maskElement)
+ .then(() => okButton.click())
+ .then(() => waitForElementInVisibility(maskElement));
+ }
+
+ waitForElementToDisappear(groupName: string) {
+ return waitForElementInVisibility(element.all(by.css('[data-name="' + groupName + '"]')));
+ }
}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/e2e/utils/e2e_util.ts b/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
index 47f01e2..92476a4 100644
--- a/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
+++ b/metron-interface/metron-alerts/e2e/utils/e2e_util.ts
@@ -47,6 +47,7 @@ export function waitForStalenessOf (_element ) {
export function loadTestData() {
deleteTestData();
+
fs.createReadStream('e2e/mock-data/alerts_ui_e2e_index.template')
.pipe(request.post('http://node1:9200/_template/alerts_ui_e2e_index'));
fs.createReadStream('e2e/mock-data/alerts_ui_e2e_index.data')
@@ -56,3 +57,14 @@ export function loadTestData() {
export function deleteTestData() {
request.delete('http://node1:9200/alerts_ui_e2e_index*');
}
+
+export function createMetaAlertsIndex() {
+ deleteMetaAlertsIndex();
+ fs.createReadStream('./../../metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/package/files/metaalert_index.template')
+ .pipe(request.post('http://node1:9200/metaalert_index'));
+}
+
+export function deleteMetaAlertsIndex() {
+ request.delete('http://node1:9200/metaalert_index*');
+}
+
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/protractor.conf.js
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/protractor.conf.js b/metron-interface/metron-alerts/protractor.conf.js
index 4fc25be..82f5c09 100644
--- a/metron-interface/metron-alerts/protractor.conf.js
+++ b/metron-interface/metron-alerts/protractor.conf.js
@@ -32,7 +32,8 @@ exports.config = {
'./e2e/alerts-list/tree-view/tree-view.e2e-spec.ts',
'./e2e/alerts-list/alert-filters/alert-filters.e2e-spec.ts',
'./e2e/alerts-list/alert-status/alerts-list-status.e2e-spec.ts',
- './e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts'
+ './e2e/alert-details/alert-status/alert-details-status.e2e-spec.ts',
+ './e2e/alerts-list/meta-alerts/meta-alert.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome',
@@ -59,6 +60,8 @@ exports.config = {
});
},
onPrepare: function() {
+ var createMetaAlertsIndex = require('./e2e/utils/e2e_util').createMetaAlertsIndex;
+ createMetaAlertsIndex();
jasmine.getEnv().addReporter(new SpecReporter());
setTimeout(function() {
browser.driver.executeScript(function() {
@@ -70,5 +73,9 @@ exports.config = {
browser.driver.manage().window().setSize(result.width, result.height);
});
});
+ },
+ onComplete: function() {
+ var createMetaAlertsIndex = require('./e2e/utils/e2e_util').createMetaAlertsIndex;
+ createMetaAlertsIndex();
}
};
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/_variables.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/_variables.scss b/metron-interface/metron-alerts/src/_variables.scss
index 21cdfdf..d7e9359 100644
--- a/metron-interface/metron-alerts/src/_variables.scss
+++ b/metron-interface/metron-alerts/src/_variables.scss
@@ -90,11 +90,13 @@ $outer-space: #2E3A3F;
$abbey: #58595B;
$white: #FFFFFF;
$iron: #D1D3D4;
+
$rolling-stone: #808285;
$nile-blue: #18404E;
$apple-blossom: #A94442;
$eastern-blue-1: #1190C0;
+$eastern-blue-2: #2192BF;
$matisse: #1E7490;
$downy: #77BBD0;
$trout: #515760;
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details-keys.pipe.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details-keys.pipe.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details-keys.pipe.ts
new file mode 100644
index 0000000..d5756da
--- /dev/null
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details-keys.pipe.ts
@@ -0,0 +1,31 @@
+/**
+ * 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 { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'alertDetailsKeys'
+})
+export class AlertDetailsKeysPipe implements PipeTransform {
+
+ transform(value: any): any {
+ let keys = value ? Object.keys(value) : [];
+ return keys.filter(field => !field.includes(':ts') && field !== 'original_string' && field !== 'comments').sort();
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
index 1b5330e..8b0efae 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.html
@@ -11,10 +11,10 @@
OR CONDITIONS OF ANY KIND, either express or implied. See the License for
the specific language governing permissions and limitations under the License.
-->
-<div class="metron-slider-pane-details load-right-to-left dialog1x">
- <div class="container-fluid pl-0 h-100">
+<div class="metron-slider-pane-details load-right-to-left dialog1x" [ngClass]="{'is-meta-alert': isMetaAlert}">
+ <div class="container-fluid pl-0 h-100" [ngClass]="{'pr-0': isMetaAlert}">
<div class="h-100 d-flex">
- <div class="nav-container">
+ <div class="nav-container" *ngIf="!isMetaAlert">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link" [ngClass]="{'active': activeTab === tabs.DETAILS}" (click)="activeTab=tabs.DETAILS">
@@ -32,7 +32,20 @@
<div class="container-fluid h-100">
<div class="row title-container">
<div class="col-md-10 px-0">
- <div class="form-title" appAlertSeverity [severity]="alertSource['threat:triage:score']"> {{ alertSource['threat:triage:score'] }} {{ alertSource.guid }}</div>
+ <div class="form-title row ml-2">
+ <div class="col px-0">
+ <span appAlertSeverity [severity]="alertSource['threat:triage:score']"> </span>
+ <span> {{ alertSource['threat:triage:score'] }} </span>
+ </div>
+ <div class="px-0" style="width: 205px">
+ <span [ngClass]="{'editable-text': alertSources.length > 1}" *ngIf="!showEditor" (click)="toggleNameEditor()"> {{ (alertSource.name && alertSource.name.length > 0)? alertSource.name : alertId | centerEllipses:20 }} </span>
+ <div class="input-group" *ngIf="showEditor">
+ <input type="text" class="form-control" [(ngModel)]="alertName">
+ <span class="input-group-addon" [ngClass]="{'disabled': alertName.length === 0}" (click)="saveName()" ><i class="fa fa-check" aria-hidden="true"></i></span>
+ <span class="input-group-addon" (click)="toggleNameEditor()"><i class="fa fa-times" aria-hidden="true"></i></span>
+ </div>
+ </div>
+ </div>
</div>
<div class="col-md-2 px-0">
<i class="fa fa-times pull-right close-button" aria-hidden="true" (click)="goBack()"></i>
@@ -57,11 +70,16 @@
</tr>
</table>
</div>
- <div class="ml-1 my-4 form" *ngIf="activeTab === tabs.DETAILS">
- <div *ngFor="let field of alertFields" class="row">
- <div class="col-6 mb-1 key">{{ field }}</div> <div class="col-6 value"> {{ alertSource[field] }} </div>
- </div>
+
+ <div class="ml-1 my-3 form" *ngIf="activeTab === tabs.DETAILS">
+ <ng-container *ngFor="let alert of alertSources; let i = index;" >
+ <div class="pb-2 alert-details-title"> Alert {{ i + 1 }} of {{ alertSources.length }}</div>
+ <div *ngFor="let field of alert | alertDetailsKeys" class="row ml-1">
+ <div class="col-6 mb-1 key">{{ field }}</div> <div class="col-6"> {{ alert[field] }} </div>
+ </div>
+ </ng-container>
</div>
+
<div *ngIf="activeTab === tabs.COMMENTS" class="my-4">
<div> Comments <span *ngIf="alertCommentsWrapper.length > 0"> ({{alertCommentsWrapper.length}}) </span></div>
<textarea class="form-control" [(ngModel)]="alertCommentStr"> </textarea>
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
index 5899140..3b10c8f 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.scss
@@ -27,6 +27,8 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ height: 35px;
+ line-height: 2.1 ;
}
.actions {
@@ -71,8 +73,30 @@
}
}
+.alert-details-title {
+ font-size: 15px;
+}
+
+.editable-text {
+ cursor: pointer;
+ border-bottom: 1px dashed $piction-blue;
+}
+
+.input-group-addon {
+ height: 35px;
+ background: #333333;
+ border: 1px solid #4D4D4D;
+ i {
+ font-size: 15px;
+ color: #999999;
+ }
+}
.dialog1x {
width: 400px;
+
+ &.is-meta-alert {
+ width: 346px;
+ }
}
.nav-container {
@@ -143,3 +167,12 @@ textarea {
.comment-container:hover i {
display: block;
}
+
+.input-group-addon {
+ cursor: pointer;
+}
+
+.disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
index 3d5b70e..8335ad7 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alert-details.component.ts
@@ -29,6 +29,7 @@ import {Patch} from '../../model/patch';
import {AlertComment} from './alert-comment';
import {AuthenticationService} from '../../service/authentication.service';
import {MetronDialogBox} from '../../shared/metron-dialog-box';
+import {META_ALERTS_INDEX, META_ALERTS_SENSOR_TYPE} from '../../utils/constants';
export enum AlertState {
NEW, OPEN, ESCALATE, DISMISS, RESOLVE
@@ -56,13 +57,17 @@ class AlertCommentWrapper {
export class AlertDetailsComponent implements OnInit {
alertId = '';
+ alertName = '';
alertSourceType = '';
+ showEditor = false;
+ isMetaAlert = false;
alertIndex = '';
alertState = AlertState;
tabs = Tabs;
activeTab = Tabs.DETAILS;
selectedAlertState: AlertState = AlertState.NEW;
alertSource: AlertSource = new AlertSource();
+ alertSources = [];
alertFields: string[] = [];
alertCommentStr = '';
alertCommentsWrapper: AlertCommentWrapper[] = [];
@@ -82,14 +87,17 @@ export class AlertDetailsComponent implements OnInit {
return false;
}
- getData() {
+ getData(fireToggleEditor = false) {
this.alertCommentStr = '';
- this.searchService.getAlert(this.alertSourceType, this.alertId).subscribe(alert => {
- this.alertSource = alert;
- this.alertFields = Object.keys(alert).filter(field => !field.includes(':ts') && field !== 'original_string' && field !== 'comments')
- .sort();
- this.selectedAlertState = this.getAlertState(alert['alert_status']);
- this.setComments(alert);
+ this.searchService.getAlert(this.alertSourceType, this.alertId).subscribe(alertSource => {
+ this.alertSource = alertSource;
+ this.selectedAlertState = this.getAlertState(alertSource['alert_status']);
+ this.alertSources = (alertSource.alert && alertSource.alert.length > 0) ? alertSource.alert : [alertSource];
+ this.setComments(alertSource);
+
+ if (fireToggleEditor) {
+ this.toggleNameEditor();
+ }
});
}
@@ -118,6 +126,7 @@ export class AlertDetailsComponent implements OnInit {
this.alertId = params['guid'];
this.alertSourceType = params['sourceType'];
this.alertIndex = params['index'];
+ this.isMetaAlert = (this.alertIndex === META_ALERTS_INDEX && this.alertSourceType !== META_ALERTS_SENSOR_TYPE) ? true : false;
this.getData();
});
};
@@ -173,6 +182,27 @@ export class AlertDetailsComponent implements OnInit {
});
}
+ toggleNameEditor() {
+ if (this.alertSources.length > 1) {
+ this.alertName = '';
+ this.showEditor = !this.showEditor;
+ }
+ }
+
+ saveName() {
+ if (this.alertName.length > 0) {
+ let patchRequest = new PatchRequest();
+ patchRequest.guid = this.alertId;
+ patchRequest.sensorType = 'metaalert';
+ patchRequest.index = META_ALERTS_INDEX;
+ patchRequest.patch = [new Patch('add', '/name', this.alertName)];
+
+ this.updateService.patch(patchRequest).subscribe(rep => {
+ this.getData(true);
+ });
+ }
+ }
+
onAddComment() {
let alertComment = new AlertComment(this.alertCommentStr, this.authenticationService.getCurrentUserName(), new Date().getTime());
let tAlertComments = this.alertCommentsWrapper.map(alertsWrapper => alertsWrapper.alertComment);
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts b/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
index d1e36a6..9722cd3 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
+++ b/metron-interface/metron-alerts/src/app/alerts/alert-details/alerts-details.module.ts
@@ -20,11 +20,13 @@ import {routing} from './alerts-details.routing';
import {SharedModule} from '../../shared/shared.module';
import {AlertDetailsComponent} from './alert-details.component';
import {AlertsService} from '../../service/alerts.service';
+import {UpdateService} from '../../service/update.service';
+import { AlertDetailsKeysPipe } from './alert-details-keys.pipe';
import {AuthenticationService} from '../../service/authentication.service';
@NgModule ({
imports: [ routing, SharedModule],
- declarations: [ AlertDetailsComponent ],
- providers: [ AuthenticationService, AlertsService ]
+ declarations: [ AlertDetailsComponent, AlertDetailsKeysPipe ],
+ providers: [ AuthenticationService, AlertsService, UpdateService ],
})
export class AlertDetailsModule { }
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.html
----------------------------------------------------------------------
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 63b4e41..611cdaf 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
@@ -27,7 +27,7 @@
<app-time-range (timeRangeChange)="onTimeRangeChange($event)" [disabled]="timeStampfilterPresent" [selectedTimeRange]="selectedTimeRange"> </app-time-range>
</span>
<span class="input-group-btn">
- <button class="btn btn-secondary btn-search" type="button" (click)="onSearch(alertSearchDirective.getSeacrhText())"></button>
+ <button class="btn btn-secondary btn-search" type="button" data-name="search" (click)="onSearch(alertSearchDirective.getSeacrhText())"></button>
</span>
</div>
</div>
@@ -61,6 +61,7 @@
<span class="dropdown-item" [class.disabled]="selectedAlerts.length == 0" (click)="processDismiss()">Dismiss</span>
<span class="dropdown-item" [class.disabled]="selectedAlerts.length == 0" (click)="processEscalate()">Escalate</span>
<span class="dropdown-item" [class.disabled]="selectedAlerts.length == 0" (click)="processResolve()">Resolve</span>
+ <span class="dropdown-item" [class.disabled]="selectedAlerts.length == 0 || isMetaAlertPresentInSelectedAlerts" (click)="processAddToAlert()">Add to Alert</span>
</div>
</div>
</div>
@@ -74,10 +75,10 @@
<app-alert-filters [facets]="searchResponse.facetCounts" (facetFilterChange)="onAddFacetFilter($event)"> </app-alert-filters>
</div>
<div class="col px-0 ml-4">
- <div class="col-sm-12 pl-0 pb-3">
- <app-group-by [facets]="searchResponse.facetCounts" (groupsChange)="onGroupsChange($event)"> </app-group-by>
+ <div class="col-xs-12 pl-0 pb-3">
+ <app-group-by [facets]="groupFacets" (groupsChange)="onGroupsChange($event)"> </app-group-by>
</div>
- <div class="col-sm-12 px-0">
+ <div class="col-xs-12 px-0">
<app-table-view #dataViewComponent
[alerts]="alerts" *ngIf="queryBuilder.groupRequest.groups.length === 0"
[queryBuilder]="queryBuilder"
@@ -96,6 +97,7 @@
[selectedAlerts]="selectedAlerts"
(onResize)="onResize()"
(onAddFilter)="onAddFilter($event)"
+ (onRefreshData)="onRefreshData($event)"
(onShowDetails)="showDetails($event)"
(onSelectedAlertsChange)="onSelectedAlertsChange($event)"></app-tree-view>
</div>
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.component.ts
----------------------------------------------------------------------
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 228c4f7..138606e 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
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import {Component, OnInit, ViewChild, ElementRef, OnDestroy, ChangeDetectorRef} from '@angular/core';
+import {Component, OnInit, ViewChild, ElementRef, OnDestroy} from '@angular/core';
import {Router, NavigationStart} from '@angular/router';
import {Observable, Subscription} from 'rxjs/Rx';
@@ -39,7 +39,9 @@ import {Filter} from '../../model/filter';
import {THREAT_SCORE_FIELD_NAME, TIMESTAMP_FIELD_NAME, ALL_TIME} from '../../utils/constants';
import {TableViewComponent} from './table-view/table-view.component';
import {Pagination} from '../../model/pagination';
-import {PatchRequest} from '../../model/patch-request';
+import {META_ALERTS_SENSOR_TYPE, META_ALERTS_INDEX} from '../../utils/constants';
+import {MetaAlertService} from '../../service/meta-alert.service';
+import {Facets} from '../../model/facets';
@Component({
selector: 'app-alerts-list',
@@ -59,6 +61,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
refreshTimer: Subscription;
pauseRefresh = false;
lastPauseRefreshValue = false;
+ isMetaAlertPresentInSelectedAlerts = false;
timeStampfilterPresent = false;
selectedTimeRange = new Filter(TIMESTAMP_FIELD_NAME, ALL_TIME, false);
threatScoreFieldName = THREAT_SCORE_FIELD_NAME;
@@ -70,6 +73,8 @@ export class AlertsListComponent implements OnInit, OnDestroy {
tableMetaData = new TableMetadata();
queryBuilder: QueryBuilder = new QueryBuilder();
pagination: Pagination = new Pagination();
+ alertChangedSubscription: Subscription;
+ groupFacets: Facets;
constructor(private router: Router,
private searchService: SearchService,
@@ -79,7 +84,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
private clusterMetaDataService: ClusterMetaDataService,
private saveSearchService: SaveSearchService,
private metronDialogBox: MetronDialogBox,
- private changeDetector: ChangeDetectorRef) {
+ private metaAlertsService: MetaAlertService) {
router.events.subscribe(event => {
if (event instanceof NavigationStart && event.url === '/alerts-list') {
this.selectedAlerts = [];
@@ -89,8 +94,12 @@ export class AlertsListComponent implements OnInit, OnDestroy {
}
addAlertChangedListner() {
- this.updateService.alertChanged$.subscribe(patchRequest => {
- this.updateAlert(patchRequest);
+ this.metaAlertsService.alertChanged$.subscribe(metaAlertAddRemoveRequest => {
+ this.updateAlert(META_ALERTS_SENSOR_TYPE, metaAlertAddRemoveRequest.metaAlertGuid, (metaAlertAddRemoveRequest.alerts === null));
+ });
+
+ this.alertChangedSubscription = this.updateService.alertChanged$.subscribe(patchRequest => {
+ this.updateAlert(patchRequest.sensorType, patchRequest.guid, false);
});
}
@@ -125,8 +134,8 @@ export class AlertsListComponent implements OnInit, OnDestroy {
}
calcColumnsToDisplay() {
- let availableWidth = document.documentElement.clientWidth - (200 + (15 * 4)); /* screenwidth - (navPaneWidth + (paddings))*/
- availableWidth = availableWidth - (55 + 25 + 25); /* availableWidth - (score + colunSelectIcon +selectCheckbox )*/
+ let availableWidth = document.documentElement.clientWidth - (200 + (15 + 15 + 25)); /* screenwidth - (navPaneWidth + (paddings))*/
+ availableWidth = availableWidth - ((20 * 3) + 55 + 25); /* availableWidth - (score + colunSelectIcon +selectCheckbox )*/
let tWidth = 0;
this.alertsColumnsToDisplay = this.alertsColumns.filter(colMetaData => {
if (colMetaData.type.toUpperCase() === 'DATE') {
@@ -145,8 +154,8 @@ export class AlertsListComponent implements OnInit, OnDestroy {
getAlertColumnNames(resetPaginationForSearch: boolean) {
Observable.forkJoin(
- this.configureTableService.getTableMetadata(),
- this.clusterMetaDataService.getDefaultColumns()
+ this.configureTableService.getTableMetadata(),
+ this.clusterMetaDataService.getDefaultColumns()
).subscribe((response: any) => {
this.prepareData(response[0], response[1], resetPaginationForSearch);
});
@@ -161,6 +170,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
ngOnDestroy() {
this.tryStopPolling();
+ this.removeAlertChangedListner();
}
ngOnInit() {
@@ -194,6 +204,8 @@ export class AlertsListComponent implements OnInit, OnDestroy {
onSelectedAlertsChange(selectedAlerts) {
this.selectedAlerts = selectedAlerts;
+ this.isMetaAlertPresentInSelectedAlerts = this.selectedAlerts.some(alert => (alert.source.alert && alert.source.alert.length > 0));
+
if (selectedAlerts.length > 0) {
this.pause();
} else {
@@ -258,29 +270,34 @@ export class AlertsListComponent implements OnInit, OnDestroy {
}
processEscalate() {
- this.updateService.updateAlertState(this.selectedAlerts, 'ESCALATE').subscribe(results => {
+ this.updateService.updateAlertState(this.selectedAlerts, 'ESCALATE', false).subscribe(results => {
this.updateSelectedAlertStatus('ESCALATE');
});
}
processDismiss() {
- this.updateService.updateAlertState(this.selectedAlerts, 'DISMISS').subscribe(results => {
+ this.updateService.updateAlertState(this.selectedAlerts, 'DISMISS', false).subscribe(results => {
this.updateSelectedAlertStatus('DISMISS');
});
}
processOpen() {
- this.updateService.updateAlertState(this.selectedAlerts, 'OPEN').subscribe(results => {
+ this.updateService.updateAlertState(this.selectedAlerts, 'OPEN', false).subscribe(results => {
this.updateSelectedAlertStatus('OPEN');
});
}
processResolve() {
- this.updateService.updateAlertState(this.selectedAlerts, 'RESOLVE').subscribe(results => {
+ this.updateService.updateAlertState(this.selectedAlerts, 'RESOLVE', false).subscribe(results => {
this.updateSelectedAlertStatus('RESOLVE');
});
}
+ processAddToAlert() {
+ this.metaAlertsService.selectedAlerts = this.selectedAlerts;
+ this.router.navigateByUrl('/alerts-list(dialog:add-to-meta-alert)');
+ }
+
removeFilter(field: string) {
this.timeStampfilterPresent = (field === TIMESTAMP_FIELD_NAME) ? false : this.timeStampfilterPresent;
this.queryBuilder.removeFilter(field);
@@ -311,7 +328,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
}
setSearchRequestSize() {
- if (this.queryBuilder.groupRequest.groups.length == 0) {
+ if (this.queryBuilder.groupRequest.groups.length === 0) {
this.queryBuilder.searchRequest.from = this.pagination.from;
if (this.tableMetaData.size) {
this.pagination.size = this.tableMetaData.size;
@@ -345,6 +362,14 @@ export class AlertsListComponent implements OnInit, OnDestroy {
this.pagination.total = results.total;
this.alerts = results.results ? results.results : [];
this.setSelectedTimeRange(this.queryBuilder.filters);
+ this.createGroupFacets(results);
+ }
+
+ private createGroupFacets(results: SearchResponse) {
+ this.groupFacets = JSON.parse(JSON.stringify(results.facetCounts));
+ if (this.groupFacets['source:type']) {
+ delete this.groupFacets['source:type']['metaalert'];
+ }
}
showConfigureTable() {
@@ -353,10 +378,12 @@ export class AlertsListComponent implements OnInit, OnDestroy {
}
showDetails(alert: Alert) {
- let url = '/alerts-list(dialog:details/' + alert.source['source:type'] + '/' + alert.source.guid + '/' + alert.index + ')';
this.selectedAlerts = [];
this.selectedAlerts = [alert];
this.saveRefreshState();
+ let sourceType = (alert.index === META_ALERTS_INDEX && !alert.source['source:type'])
+ ? META_ALERTS_SENSOR_TYPE : alert.source['source:type'];
+ let url = '/alerts-list(dialog:details/' + sourceType + '/' + alert.source.guid + '/' + alert.index + ')';
this.router.navigateByUrl(url);
}
@@ -405,13 +432,22 @@ export class AlertsListComponent implements OnInit, OnDestroy {
this.searchService.interval = this.refreshInterval;
}
- updateAlert(patchRequest: PatchRequest) {
- this.searchService.getAlert(patchRequest.sensorType, patchRequest.guid).subscribe(alertSource => {
- this.alerts.filter(alert => alert.source.guid === patchRequest.guid)
+ updateAlert(sensorType: string, guid: string, isDelete: boolean) {
+ if (isDelete) {
+ let alertIndex = -1;
+ this.alerts.forEach((alert, index) => {
+ alertIndex = (alert.source.guid === guid) ? index : alertIndex;
+ });
+ this.alerts.splice(alertIndex, 1);
+ return;
+ }
+
+ this.searchService.getAlert(sensorType, guid).subscribe(alertSource => {
+ this.alerts.filter(alert => alert.source.guid === guid)
.map(alert => alert.source = alertSource);
});
}
-
+
updateSelectedAlertStatus(status: string) {
for (let selectedAlert of this.selectedAlerts) {
selectedAlert.source['alert_status'] = status;
@@ -420,4 +456,7 @@ export class AlertsListComponent implements OnInit, OnDestroy {
this.resume();
}
+ removeAlertChangedListner() {
+ this.alertChangedSubscription.unsubscribe();
+ }
}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alerts-list/alerts-list.module.ts
----------------------------------------------------------------------
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 6e0dd2a..d1c3cc0 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
@@ -21,7 +21,6 @@ import {DecimalPipe} from '@angular/common';
import {AlertsListComponent} from './alerts-list.component';
import {routing} from './alerts-list.routing';
import {SharedModule} from '../../shared/shared.module';
-import {SearchService} from '../../service/search.service';
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';
@@ -38,7 +37,7 @@ import {TreeViewComponent} from './tree-view/tree-view.component';
ListGroupModule, CollapseModule, GroupByModule, TimeRangeModule],
exports: [AlertsListComponent],
declarations: [AlertsListComponent, TableViewComponent, TreeViewComponent, AlertFiltersComponent],
- providers: [DecimalPipe, SearchService]
+ providers: [DecimalPipe]
})
export class AlertsListModule {
}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alerts-list/query-builder.ts
----------------------------------------------------------------------
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 e9f96eb..9ac5f6e 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
@@ -147,10 +147,7 @@ export class QueryBuilder {
}
setSort(sortBy: string, order: string) {
- let sortField = new SortField();
- sortField.field = sortBy;
- sortField.sortOrder = order;
-
+ let sortField = new SortField(sortBy, order);
this.searchRequest.sort = [sortField];
}
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
index b8fd14f..d2b1108 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.html
@@ -15,23 +15,98 @@
<table class="table table-sm" metron-config-table [data]="alerts" [cellSelectable]="true" (onSort)="onSort($event)" style="white-space: nowrap;" (window:resize)="resize()" #table>
<thead>
<tr>
- <th style="width:55px"> <metron-config-sorter [type]="'number'" [sortBy]="threatScoreFieldName"> Score </metron-config-sorter> </th>
+ <th width="15" class="dropdown-cell"> </th>
+ <th width="55"> <metron-config-sorter [type]="'number'" [sortBy]="threatScoreFieldName"> Score </metron-config-sorter> </th>
<th *ngFor="let column of alertsColumnsToDisplay" [id]="column.name"> <metron-config-sorter [type]="column.type" [sortBy]="column.name" title="{{column.name}}"> {{ column.name | columnNameTranslate | centerEllipses:15 }}</metron-config-sorter> </th>
- <th></th>
- <th style="width:25px"><input id="select-deselect-all" class="fontawesome-checkbox" type="checkbox" (click)="selectAllRows($event)"><label for="select-deselect-all"></label></th>
+ <th width="20" class="icon-cell"></th>
+ <th width="20" class="icon-cell"></th>
+ <th width="25"><input id="select-deselect-all" class="fontawesome-checkbox" type="checkbox" (click)="selectAllRows($event)"><label for="select-deselect-all"></label></th>
</tr>
</thead>
<tbody>
- <tr *ngFor="let alert of alerts" (click)="showDetails($event, alert)" [ngClass]="{'selected' : selectedAlerts.indexOf(alert) != -1}">
- <td (click)="addFilter(threatScoreFieldName, alert.source[threatScoreFieldName])">
- <div appAlertSeverity [severity]="alert.source[threatScoreFieldName]"> <a> {{ alert.source[threatScoreFieldName] ? alert.source[threatScoreFieldName] : '-' }} </a> </div>
- </td>
- <td *ngFor="let column of alertsColumnsToDisplay" #cell>
- <a (click)="addFilter(column.name, getValue(alert, column, false))" title="{{getValue(alert, column, true)}}" style="color:#689AA9">{{ getValue(alert, column, true) | centerEllipses:20:cell }}</a>
- </td>
- <td> <i class="fa fa-comments-o" aria-hidden="true" *ngIf="alert.source.comments && alert.source.comments.length > 0"></i> </td>
- <td><input id="{{ alert.id }}" class="fontawesome-checkbox" type="checkbox" name="{{alert.id}}" (click)="selectRow($event, alert)" [checked]="selectedAlerts.indexOf(alert) != -1"><label attr.for="{{ alert.id }}"></label></td>
- </tr>
+ <ng-container *ngFor="let alert of alerts; let alertIndex = index;">
+
+ <ng-container *ngIf="!alert.source.alert || alert.source.alert.length === 0">
+ <tr (click)="showDetails($event, alert)" [ngClass]="{'selected' : selectedAlerts.indexOf(alert) != -1}">
+ <td width="15" class="icon-cell"></td>
+ <td (click)="addFilter(threatScoreFieldName, alert.source[threatScoreFieldName])">
+ <div appAlertSeverity [severity]="alert.source[threatScoreFieldName]">
+ <a> {{ alert.source[threatScoreFieldName] ? alert.source[threatScoreFieldName] : '-' }} </a>
+ </div>
+ </td>
+ <td *ngFor="let column of alertsColumnsToDisplay" #cell>
+ <a (click)="addFilter(column.name, getValue(alert, column, false))" title="{{getValue(alert, column, true)}}" style="color:#689AA9">
+ {{ getValue(alert,column, true) | centerEllipses:20:cell }}
+ </a>
+ </td>
+ <td width="20" class="icon-cell"></td>
+ <td width="20" class="icon-cell">
+ <i class="fa fa-comments-o" aria-hidden="true" *ngIf="alert.source.comments && alert.source.comments.length > 0"></i>
+ </td>
+ <td>
+ <input id="{{ alert.id }}" class="fontawesome-checkbox" type="checkbox" name="{{alert.id}}" (click)="selectRow($event, alert)" [checked]="selectedAlerts.indexOf(alert) != -1">
+ <label attr.for="{{ alert.id }}"></label>
+ </td>
+ </tr>
+ </ng-container>
+
+ <ng-container *ngIf="alert.source.alert && alert.source.alert.length > 0">
+ <tr (click)="showDetails($event, alert)" [ngClass]="{'selected' : selectedAlerts.indexOf(alert) != -1}">
+ <td width="15" class="icon-cell dropdown-cell" (click)="toggleExpandCollapse($event, alert)">
+ <i class="fa" aria-hidden="true"
+ [ngClass]="{'fa-caret-right': metaAlertsDisplayState[alert.id] === metronAlertDisplayState.COLLAPSE, 'fa-caret-down': metaAlertsDisplayState[alert.id] === metronAlertDisplayState.EXPAND}">
+ </i>
+ </td>
+ <td (click)="addFilter(threatScoreFieldName, alert.source[threatScoreFieldName])">
+ <span appAlertSeverity [severity]="alert.source[threatScoreFieldName]"> <a> {{ alert.source[threatScoreFieldName] ? alert.source[threatScoreFieldName] : '-' }} </a> </span>
+ </td>
+ <td [attr.colspan]="alertsColumnsToDisplay.length - 1">
+ <a (click)="addFilter('guid', alert.id)" [attr.title]="alert.id" style="color:#689AA9"> {{ alert.source['name'] ? alert.source['name'] : alert.id | centerEllipses:20:cell }}</a>
+ <span> ({{ alert.source.alert.length }})</span>
+ </td>
+ <td>
+ <a *ngIf="isStatusFieldPresent" (click)="addFilter('alert_status', alert.source['alert_status'])" style="color:#689AA9">
+ {{ alert.source['alert_status'] ?alert.source['alert_status'] : 'New' | centerEllipses:20:cell }}
+ </a>
+ </td>
+ <td width="20" class="icon-cell" (click)="deleteMetaAlert($event, alert, alertIndex)">
+ <i class="fa fa-chain-broken" aria-hidden="true"></i>
+ </td>
+ <td width="20" class="icon-cell">
+ <i class="fa fa-comments-o" aria-hidden="true" *ngIf="alert.source.comments && alert.source.comments.length > 0"></i>
+ </td>
+ <td>
+ <input id="{{ alert.id }}" class="fontawesome-checkbox" type="checkbox" name="{{alert.id}}" (click)="selectRow($event, alert)" [checked]="selectedAlerts.indexOf(alert) != -1">
+ <label attr.for="{{ alert.id }}"></label>
+ </td>
+ </tr>
+ <tr *ngFor="let metaAlerts of alert.source.alert; let metaAlertIndex = index;" (click)="showMetaAlertDetails($event, metaAlerts)"
+ [ngClass]="{'selected' : selectedAlerts.indexOf(metaAlerts) != -1 , 'd-none': metaAlertsDisplayState[alert.id] === metronAlertDisplayState.COLLAPSE}">
+ <td width="15" class="icon-cell" class="dropdown-cell"></td>
+ <td (click)="addFilter(threatScoreFieldName, alert.source[threatScoreFieldName])" style="padding-left: 15px">
+ <div appAlertSeverity [severity]="metaAlerts[threatScoreFieldName]">
+ <a> {{ metaAlerts[threatScoreFieldName] ? metaAlerts[threatScoreFieldName] : '-' }} </a>
+ </div>
+ </td>
+ <td *ngFor="let column of alertsColumnsToDisplay">
+ <a *ngIf="column.name !== 'alert_status'" (click)="addFilter(column.name, getValueFromSource(metaAlerts, column, false))" title="{{ getValueFromSource(metaAlerts, column, true) }}" style="color:#689AA9">
+ {{ getValueFromSource(metaAlerts, column, true) | centerEllipses:20:cell }}
+ </a>
+ <a *ngIf="column.name === 'alert_status'" (click)="addFilter(column.name, getValue(alert, column, false))" title="{{getValue(alert, column, true)}}" style="color:#689AA9">
+ {{ getValue(alert,column, true) | centerEllipses:20:cell }}
+ </a>
+ </td>
+ <td width="20" class="icon-cell" (click)="deleteOneAlertFromMetaAlert($event, alert, metaAlertIndex)">
+ <i class="fa fa-chain-broken" aria-hidden="true"></i>
+ </td>
+ <td width="20" class="icon-cell">
+ <i class="fa fa-comments-o" aria-hidden="true" *ngIf="metaAlerts.comments && metaAlerts.comments.length > 0"></i>
+ </td>
+ <td></td>
+ </tr>
+ </ng-container>
+
+ </ng-container>
</tbody>
</table>
</div>
http://git-wip-us.apache.org/repos/asf/metron/blob/d07833a2/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.scss
----------------------------------------------------------------------
diff --git a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.scss b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.scss
index eec7f92..f648ab2 100644
--- a/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.scss
+++ b/metron-interface/metron-alerts/src/app/alerts/alerts-list/table-view/table-view.component.scss
@@ -24,4 +24,17 @@
.configure-table-icon {
font-size: 16px;
cursor: pointer;
-}
\ No newline at end of file
+}
+
+.fa-chain-broken {
+ color: $piction-blue;
+}
+
+.dropdown-cell {
+ padding-left: 0.6rem;
+}
+
+.icon-cell {
+ padding: 0.3rem 3px;
+}
+