diff --git a/cypress/integration/acknowledge_alerts_modal_spec.js b/cypress/integration/acknowledge_alerts_modal_spec.js new file mode 100644 index 000000000..daf439b05 --- /dev/null +++ b/cypress/integration/acknowledge_alerts_modal_spec.js @@ -0,0 +1,192 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import { INDEX, PLUGIN_NAME } from '../support/constants'; +import sampleAlertsFlyoutBucketMonitor from '../fixtures/sample_alerts_flyout_bucket_level_monitor.json'; +import sampleAlertsFlyoutQueryMonitor from '../fixtures/sample_alerts_flyout_query_level_monitor.json'; + +const BUCKET_MONITOR = 'sample_alerts_flyout_bucket_level_monitor'; +const BUCKET_TRIGGER = 'sample_alerts_flyout_bucket_level_trigger'; +const QUERY_MONITOR = 'sample_alerts_flyout_query_level_monitor'; +const QUERY_TRIGGER = 'sample_alerts_flyout_query_level_trigger'; + +const TWENTY_SECONDS = 20000; + +describe('AcknowledgeAlertsModal', () => { + before(() => { + // Delete any existing monitors + cy.deleteAllMonitors(); + + // Load sample data + cy.loadSampleEcommerceData(); + + // Create the test monitors + cy.createMonitor(sampleAlertsFlyoutBucketMonitor); + cy.createMonitor(sampleAlertsFlyoutQueryMonitor); + + // Visit Alerting OpenSearch Dashboards + cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/monitors`); + + // Confirm test monitors were created successfully + cy.contains(BUCKET_MONITOR, { timeout: TWENTY_SECONDS }); + cy.contains(QUERY_MONITOR, { timeout: TWENTY_SECONDS }); + + // Wait 1 minute for the test monitors to trigger alerts, then go to the 'Alerts by trigger' dashboard page to view alerts + cy.wait(60000); + }); + + beforeEach(() => { + // Reloading the page to close any modals that were not closed by other tests that had failures. + cy.visit(`${Cypress.env('opensearch_dashboards')}/app/${PLUGIN_NAME}#/dashboard`); + + // Confirm dashboard is displaying rows for the test monitors. + cy.contains(BUCKET_MONITOR, { timeout: TWENTY_SECONDS }); + cy.contains(QUERY_MONITOR, { timeout: TWENTY_SECONDS }); + }); + + it('Acknowledge button disabled when more than 1 trigger selected', () => { + // Set the 'severity' filter to only display rows with ACTIVE alerts. + cy.get('[data-test-subj="dashboardAlertStateFilter"]').select('Active'); + + // Confirm the 'Alerts by trigger' dashboard contains more than 1 row. + cy.get('tbody > tr', { timeout: TWENTY_SECONDS }).should(($tr) => + expect($tr).to.have.length.greaterThan(1) + ); + + // Select the first and last rows in the table. + cy.get('input[data-test-subj^="checkboxSelectRow-"]').first().click(); + cy.get('input[data-test-subj^="checkboxSelectRow-"]').last().click(); + + // Click the 'Alerts by trigger' dashboard 'Acknowledge' button. + cy.get('[data-test-subj="acknowledgeAlertsButton"]').should('be.disabled'); + }); + + it('Bucket-level monitor modal test', () => { + // To simplify testing, filter the dashboard to only display the desired trigger. + cy.get(`input[type="search"]`).focus().type(BUCKET_TRIGGER); + + // Confirm the dashboard is displaying only 1 row. + cy.get('tbody > tr', { timeout: TWENTY_SECONDS }).should(($tr) => + expect($tr).to.have.length(1) + ); + + // Find the row for the trigger, and check off the checkbox. + cy.get('input[data-test-subj^="checkboxSelectRow-"]').first().click(); + + // Click the 'Alerts by trigger' dashboard 'Acknowledge' button. + cy.get('[data-test-subj="acknowledgeAlertsButton"]').click(); + + // Perform the test checks within the modal component. + cy.get(`[data-test-subj="alertsDashboardModal_${BUCKET_TRIGGER}"]`).within(() => { + // Confirm modal header contains expected text. + cy.get(`[data-test-subj="alertsDashboardModal_header_${BUCKET_TRIGGER}"]`).contains( + `Select which alerts to acknowledge for ${BUCKET_TRIGGER}` + ); + + // Set the 'severity' filter to only display ACTIVE alerts. + cy.get('[data-test-subj="dashboardAlertStateFilter"]').select('Active'); + + // This monitor configuration consistently returns 46 alerts when testing locally. + // Confirm the modal dashboard contains more than 1 ACTIVE alert. + cy.get('tbody > tr', { timeout: TWENTY_SECONDS }).should(($tr) => + expect($tr).to.have.length.greaterThan(1) + ); + + // Select the first and last alerts in the table. + cy.get('input[data-test-subj^="checkboxSelectRow-"]', { timeout: TWENTY_SECONDS }) + .first() + .click(); + cy.get('input[data-test-subj^="checkboxSelectRow-"]', { timeout: TWENTY_SECONDS }) + .last() + .click(); + + // Press the modal 'Acknowledge button, and wait for the AcknowledgeAlerts API call to complete. + cy.get('[data-test-subj="alertsDashboardModal_acknowledgeAlertsButton"]').click(); + }); + + // Confirm acknowledge alerts toast displays expected text. + cy.contains('Successfully acknowledged 2 alerts.'); + + // Confirm alerts were acknowledged as expected. + cy.get(`[data-test-subj="alertsDashboardModal_${BUCKET_TRIGGER}"]`).within(() => { + // Set the 'severity' filter to only display ACKNOWLEDGED alerts. + cy.get('[data-test-subj="dashboardAlertStateFilter"]').select('Acknowledged'); + + // Confirm the table displays 2 acknowledged alerts. + cy.get('tbody > tr', { timeout: TWENTY_SECONDS }).should(($tr) => + expect($tr).to.have.length(2) + ); + }); + + // Confirm close button hides the modal. + cy.get(`[data-test-subj="alertsDashboardModal_closeButton_${BUCKET_TRIGGER}"]`).click(); + cy.contains(`[data-test-subj="alertsDashboardModal_${BUCKET_TRIGGER}"]`).should('not.exist'); + }); + + it('Query-level monitor modal test', () => { + // To simplify testing, filter the dashboard to only display the desired trigger. + cy.get(`input[type="search"]`).focus().type(QUERY_TRIGGER); + + // Confirm the dashboard is displaying only 1 row. + cy.get('tbody > tr', { timeout: TWENTY_SECONDS }).should(($tr) => + expect($tr).to.have.length(1) + ); + + // Find the row for the trigger, and check off the checkbox. + cy.get('input[data-test-subj^="checkboxSelectRow-"]').first().click(); + + // Click the 'Alerts by trigger' dashboard 'Acknowledge' button. + cy.get('[data-test-subj="acknowledgeAlertsButton"]').click(); + + // Perform the test checks within the modal component. + cy.get(`[data-test-subj="alertsDashboardModal_${QUERY_TRIGGER}"]`).within(() => { + // Confirm modal header contains expected text. + cy.get(`[data-test-subj="alertsDashboardModal_header_${QUERY_TRIGGER}"]`).contains( + `Select which alerts to acknowledge for ${QUERY_TRIGGER}` + ); + + // Set the 'severity' filter to only display ACTIVE alerts. + cy.get('[data-test-subj="dashboardAlertStateFilter"]').select('Active'); + + // Confirm the modal dashboard contains 1 alert. + cy.get('tbody > tr', { timeout: TWENTY_SECONDS }).should(($tr) => + expect($tr).to.have.length(1) + ); + + // Select the alert. + cy.get('input[data-test-subj^="checkboxSelectRow-"]').first().click(); + + // Press the modal 'Acknowledge' button, and wait for the AcknowledgeAlerts API call to complete. + cy.get('[data-test-subj="alertsDashboardModal_acknowledgeAlertsButton"]').click(); + }); + + // Confirm acknowledge alerts toast displays expected text. + cy.contains('Successfully acknowledged 1 alert.'); + + // Confirm alerts were acknowledged as expected. + cy.get(`[data-test-subj="alertsDashboardModal_${QUERY_TRIGGER}"]`).within(() => { + // Set the 'severity' filter to only display ACKNOWLEDGED alerts. + cy.get('[data-test-subj="dashboardAlertStateFilter"]').select('Acknowledged'); + + // Confirm the table displays 1 acknowledged alert. + cy.get('tbody > tr', { timeout: TWENTY_SECONDS }).should(($tr) => + expect($tr).to.have.length(1) + ); + }); + + // Confirm close button hides the modal. + cy.get(`[data-test-subj="alertsDashboardModal_closeButton_${QUERY_TRIGGER}"]`).click(); + cy.contains(`[data-test-subj="alertsDashboardModal_${QUERY_TRIGGER}"]`).should('not.exist'); + }); + + after(() => { + // Delete all monitors + cy.deleteAllMonitors(); + + // Delete sample data + cy.deleteIndexByName(`${INDEX.SAMPLE_DATA_ECOMMERCE}`); + }); +}); diff --git a/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.js b/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.js new file mode 100644 index 000000000..9ebb84b46 --- /dev/null +++ b/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.js @@ -0,0 +1,454 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { Component } from 'react'; +import _ from 'lodash'; +import queryString from 'query-string'; +import PropTypes from 'prop-types'; +import { + EuiBasicTable, + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiIcon, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiOverlayMask, +} from '@elastic/eui'; +import { + ALERT_STATE, + MONITOR_ACTIONS, + MONITOR_GROUP_BY, + MONITOR_INPUT_DETECTOR_ID, + MONITOR_TYPE, + OPENSEARCH_DASHBOARDS_AD_PLUGIN, +} from '../../../../utils/constants'; +import { + displayAcknowledgedAlertsToast, + filterActiveAlerts, + getQueryObjectFromState, + getURLQueryParams, + insertGroupByColumn, + removeColumns, +} from '../../utils/helpers'; +import { backendErrorNotification } from '../../../../utils/helpers'; +import { MAX_ALERT_COUNT } from '../../utils/constants'; +import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../../Monitors/containers/Monitors/utils/constants'; +import DashboardControls from '../DashboardControls'; +import ContentPanel from '../../../../components/ContentPanel'; +import { queryColumns } from '../../utils/tableUtils'; +import DashboardEmptyPrompt from '../DashboardEmptyPrompt'; + +export const DEFAULT_NUM_MODAL_ROWS = 10; + +export default class AcknowledgeAlertsModal extends Component { + constructor(props) { + super(props); + const { location, monitor_id } = this.props; + + const { + alertState, + from, + search, + severityLevel, + size, + sortDirection, + sortField, + } = getURLQueryParams(location); + + this.state = { + alerts: [], + alertState: alertState, + loading: true, + monitors: [], + monitorIds: [monitor_id], + page: Math.floor(from / size), + search: search, + selectedItems: [], + severityLevel: severityLevel, + size: DEFAULT_NUM_MODAL_ROWS, + sortDirection: sortDirection, + sortField: sortField, + totalAlerts: 0, + }; + } + + componentDidMount() { + const { + alertState, + page, + search, + severityLevel, + size, + sortDirection, + sortField, + monitorIds, + } = this.state; + this.getAlerts( + page * size, + size, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds + ); + } + + componentDidUpdate(prevProps, prevState) { + const prevQuery = getQueryObjectFromState(prevState); + const currQuery = getQueryObjectFromState(this.state); + if (!_.isEqual(prevQuery, currQuery)) { + const { + page, + size, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds, + } = this.state; + this.getAlerts( + page * size, + size, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds + ); + } + } + + getAlerts = async () => { + this.setState({ ...this.state, loading: true }); + const { + from, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds, + } = this.state; + + const { httpClient, history, notifications, triggerId } = this.props; + + const params = { + from, + size: MAX_ALERT_COUNT, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds, + }; + + const queryParamsString = queryString.stringify(params); + history.replace({ ...this.props.location, search: queryParamsString }); + + httpClient.get('../api/alerting/alerts', { query: params }).then((resp) => { + if (resp.ok) { + const { alerts } = resp; + const filteredAlerts = _.filter(alerts, { trigger_id: triggerId }); + this.setState({ + ...this.state, + alerts: filteredAlerts, + totalAlerts: filteredAlerts.length, + }); + } else { + console.log('error getting alerts:', resp); + backendErrorNotification(notifications, 'get', 'alerts', resp.err); + } + }); + + this.setState({ ...this.state, loading: false }); + }; + + acknowledgeAlerts = async () => { + const { selectedItems } = this.state; + const { httpClient, notifications } = this.props; + + if (!selectedItems.length) return; + + const selectedAlerts = filterActiveAlerts(selectedItems); + + const monitorAlerts = selectedAlerts.reduce((monitorAlerts, alert) => { + const { id, monitor_id: monitorId } = alert; + if (monitorAlerts[monitorId]) monitorAlerts[monitorId].push(id); + else monitorAlerts[monitorId] = [id]; + return monitorAlerts; + }, {}); + + Object.entries(monitorAlerts).map(([monitorId, alerts]) => + httpClient + .post(`../api/alerting/monitors/${monitorId}/_acknowledge/alerts`, { + body: JSON.stringify({ alerts }), + }) + .then((resp) => { + if (!resp.ok) { + backendErrorNotification(notifications, 'acknowledge', 'alert', resp.resp); + } else { + const successfulCount = _.get(resp, 'resp.success', []).length; + displayAcknowledgedAlertsToast(notifications, successfulCount); + } + }) + .catch((error) => error) + ); + + const { + page, + size, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds, + } = this.state; + await this.getAlerts( + page * size, + size, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds + ); + this.setState({ ...this.state, selectedItems: [] }); + }; + + onSeverityLevelChange = (e) => { + this.setState({ page: 0, severityLevel: e.target.value }); + }; + + onAlertStateChange = (e) => { + this.setState({ page: 0, alertState: e.target.value }); + }; + + onPageClick = (page) => { + this.setState({ page }); + }; + + onSearchChange = (e) => { + this.setState({ page: 0, search: e.target.value }); + }; + + onSelectionChange = (selectedItems) => { + this.setState({ selectedItems }); + }; + + onTableChange = ({ page: tablePage = {}, sort = {} }) => { + const { index: page, size } = tablePage; + + const { field: sortField, direction: sortDirection } = sort; + this.setState({ + page, + size, + sortField, + sortDirection, + }); + + const { alerts } = this.props; + this.setState({ alerts }); + }; + + onCreateTrigger = () => { + const { history, monitorId, onClose } = this.props; + onClose(); + history.push(`/monitors/${monitorId}?action=${MONITOR_ACTIONS.UPDATE_MONITOR}`); + }; + + render() { + const { monitor, onClose, triggerName } = this.props; + const detectorId = _.get(monitor, MONITOR_INPUT_DETECTOR_ID); + const groupBy = _.get(monitor, MONITOR_GROUP_BY); + const monitorType = _.get(monitor, 'monitor_type', MONITOR_TYPE.QUERY_LEVEL); + + const actions = () => { + const { selectedItems } = this.state; + const actions = [ + + Acknowledge + , + ]; + if (!_.isEmpty(detectorId)) { + actions.unshift( + + View detector + + ); + } + return actions; + }; + + const getItemId = (item) => { + switch (monitorType) { + case MONITOR_TYPE.QUERY_LEVEL: + return `${item.id}-${item.version}`; + case MONITOR_TYPE.BUCKET_LEVEL: + return item.id; + } + }; + + const { + alerts = [], + alertState, + loading, + page, + search, + selectable, + selectedItems, + severityLevel, + size, + sortDirection, + sortField, + totalAlerts, + } = this.state; + + const columnType = () => { + let columns = []; + switch (monitorType) { + case MONITOR_TYPE.QUERY_LEVEL: + columns = queryColumns; + break; + case MONITOR_TYPE.BUCKET_LEVEL: + columns = insertGroupByColumn(groupBy); + break; + } + return removeColumns(['trigger_name'], columns); + }; + + const pagination = { + pageIndex: page, + pageSize: size, + totalItemCount: totalAlerts, + pageSizeOptions: DEFAULT_PAGE_SIZE_OPTIONS, + }; + + const selection = { + onSelectionChange: this.onSelectionChange, + selectable: (item) => item.state === ALERT_STATE.ACTIVE, + selectableMessage: (selectable) => + selectable ? undefined : 'Only active alerts can be acknowledged.', + }; + + const sorting = { + sort: { + direction: sortDirection, + field: sortField, + }, + }; + + const trimmedAlerts = alerts.slice(page * size, page * size + size); + + return ( + + + + + {`Select which alerts to acknowledge for ${triggerName}`} + + + + + + + + + + + + ) + } + data-test-subj={`alertsDashboardModal_table_${triggerName}`} + /> + + + + + + + Close + + + + + ); + } +} + +AcknowledgeAlertsModal.propTypes = { + httpClient: PropTypes.object.isRequired, + history: PropTypes.object.isRequired, + location: PropTypes.object.isRequired, + monitor: PropTypes.object.isRequired, + monitorId: PropTypes.string.isRequired, + notifications: PropTypes.object.isRequired, + triggerId: PropTypes.string.isRequired, + triggerName: PropTypes.string.isRequired, + onClose: PropTypes.func.isRequired, +}; diff --git a/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.test.js b/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.test.js new file mode 100644 index 000000000..8c7678b73 --- /dev/null +++ b/public/pages/Dashboard/components/AcknowledgeAlertsModal/AcknowledgeAlertsModal.test.js @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import _ from 'lodash'; +import { historyMock, httpClientMock } from '../../../../../test/mocks'; +import AcknowledgeAlertsModal from './AcknowledgeAlertsModal'; +import { FORMIK_INITIAL_VALUES } from '../../../CreateMonitor/containers/CreateMonitor/utils/constants'; +import coreMock from '../../../../../test/mocks/CoreMock'; + +beforeEach(() => { + jest.clearAllMocks(); +}); + +describe('AcknowledgeAlertsModal', () => { + test('renders', () => { + const location = { + hash: '', + pathname: '/dashboard', + search: '', + state: undefined, + }; + const notifications = _.cloneDeep(coreMock.notifications); + const component = ( + {}} + /> + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap b/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap new file mode 100644 index 000000000..1f8fd9c33 --- /dev/null +++ b/public/pages/Dashboard/components/AcknowledgeAlertsModal/__snapshots__/AcknowledgeAlertsModal.test.js.snap @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AcknowledgeAlertsModal renders 1`] = ` + +`; diff --git a/public/pages/Dashboard/components/AcknowledgeAlertsModal/index.js b/public/pages/Dashboard/components/AcknowledgeAlertsModal/index.js new file mode 100644 index 000000000..559466507 --- /dev/null +++ b/public/pages/Dashboard/components/AcknowledgeAlertsModal/index.js @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import AcknowledgeAlertsModal from './AcknowledgeAlertsModal'; + +export default AcknowledgeAlertsModal; diff --git a/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js b/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js index ee52c205c..c1ca17d80 100644 --- a/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js +++ b/public/pages/Dashboard/components/DashboardEmptyPrompt/DashboardEmptyPrompt.js @@ -14,6 +14,9 @@ const createMonitorText = 'There are no existing alerts. Create a monitor to add triggers and actions. Once an alarm is triggered, the state will show in this table.'; const createTriggerText = 'There are no existing alerts. Create a trigger to start alerting. Once an alarm is triggered, the state will show in this table.'; +const editTriggerConditionsText = + 'There are no existing alerts. Adjust trigger conditions to start alerting. Once an alarm is triggered, the state will show in this table.'; + const createMonitorButton = ( Create monitor @@ -25,17 +28,24 @@ const editMonitorButton = (onCreateTrigger) => ( ); -const DashboardEmptyPrompt = ({ onCreateTrigger }) => { +const DashboardEmptyPrompt = ({ onCreateTrigger, isModal = false }) => { const inMonitorDetails = typeof onCreateTrigger === 'function'; + const displayText = isModal + ? editTriggerConditionsText + : inMonitorDetails + ? createTriggerText + : createMonitorText; return ( -

{inMonitorDetails ? createTriggerText : createMonitorText}

+

{displayText}

} - actions={inMonitorDetails ? editMonitorButton(onCreateTrigger) : createMonitorButton} + actions={ + inMonitorDetails || isModal ? editMonitorButton(onCreateTrigger) : createMonitorButton + } /> ); }; diff --git a/public/pages/Dashboard/containers/Dashboard.js b/public/pages/Dashboard/containers/Dashboard.js index 5e3908bd1..a170b465e 100644 --- a/public/pages/Dashboard/containers/Dashboard.js +++ b/public/pages/Dashboard/containers/Dashboard.js @@ -18,8 +18,6 @@ import { } from '../../../utils/constants'; import { backendErrorNotification } from '../../../utils/helpers'; import { - displayAcknowledgedAlertsToast, - filterActiveAlerts, getInitialSize, getQueryObjectFromState, getURLQueryParams, @@ -28,8 +26,7 @@ import { } from '../utils/helpers'; import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../Monitors/containers/Monitors/utils/constants'; import { MAX_ALERT_COUNT } from '../utils/constants'; - -// TODO: Abstract out a Table component to be used in both Dashboard and Monitors +import AcknowledgeAlertsModal from '../components/AcknowledgeAlertsModal'; export default class Dashboard extends Component { constructor(props) { @@ -59,6 +56,7 @@ export default class Dashboard extends Component { search, selectedItems: [], severityLevel, + showAlertsModal: false, size: getInitialSize(perAlertView, size), sortDirection, sortField, @@ -141,10 +139,7 @@ export default class Dashboard extends Component { httpClient.get('../api/alerting/alerts', { query: params }).then((resp) => { if (resp.ok) { const { alerts, totalAlerts } = resp; - this.setState({ - alerts, - totalAlerts, - }); + this.setState({ alerts, totalAlerts }); if (!perAlertView) { const alertsByTriggers = groupAlertsByTrigger(alerts); @@ -194,62 +189,6 @@ export default class Dashboard extends Component { this.setState({ ...this.state, loadingMonitors: false, monitors: monitors }); } - // TODO: exists in both Dashboard and Monitors, should be moved to redux when implemented - acknowledgeAlert = async () => { - const { selectedItems } = this.state; - const { httpClient, notifications, perAlertView } = this.props; - - if (!selectedItems.length) return; - - let selectedAlerts = perAlertView ? selectedItems : _.get(selectedItems, '0.alerts', []); - selectedAlerts = filterActiveAlerts(selectedAlerts); - - const monitorAlerts = selectedAlerts.reduce((monitorAlerts, alert) => { - const { id, monitor_id: monitorId } = alert; - if (monitorAlerts[monitorId]) monitorAlerts[monitorId].push(id); - else monitorAlerts[monitorId] = [id]; - return monitorAlerts; - }, {}); - - Object.entries(monitorAlerts).map(([monitorId, alerts]) => - httpClient - .post(`../api/alerting/monitors/${monitorId}/_acknowledge/alerts`, { - body: JSON.stringify({ alerts }), - }) - .then((resp) => { - if (!resp.ok) { - backendErrorNotification(notifications, 'acknowledge', 'alert', resp.resp); - } else { - const successfulCount = _.get(resp, 'resp.success', []).length; - displayAcknowledgedAlertsToast(notifications, successfulCount); - } - }) - .catch((error) => error) - ); - - const { - page, - size, - search, - sortField, - sortDirection, - severityLevel, - alertState, - monitorIds, - } = this.state; - this.getAlerts( - page * size, - size, - search, - sortField, - sortDirection, - severityLevel, - alertState, - monitorIds - ); - this.setState({ selectedItems: [] }); - }; - onTableChange = ({ page: tablePage = {}, sort = {} }) => { const { index: page, size } = tablePage; const { field: sortField, direction: sortDirection } = sort; @@ -312,6 +251,35 @@ export default class Dashboard extends Component { ); }; + openModal = () => { + this.setState({ ...this.state, showAlertsModal: true }); + }; + + closeModal = () => { + this.setState({ ...this.state, search: '', showAlertsModal: false }); + this.refreshDashboard(); + }; + + renderModal = () => { + const { history, httpClient, location, notifications } = this.props; + const { monitors, selectedItems } = this.state; + const { monitor_id, triggerID, trigger_name } = selectedItems[0]; + const monitor = _.get(_.find(monitors, { _id: monitor_id }), '_source'); + return ( + + ); + }; + render() { const { alerts, @@ -392,7 +360,7 @@ export default class Dashboard extends Component { // The acknowledge button is disabled when viewing by per alerts, and no item selected or per trigger view and item selected is not 1. const actions = [ @@ -480,7 +448,10 @@ export default class Dashboard extends Component { selection={selection} onChange={this.onTableChange} noItemsMessage={} + data-test-subj={'alertsDashboard_table'} /> + + {this.state.showAlertsModal && this.renderModal()} ); } diff --git a/public/pages/Dashboard/containers/__snapshots__/Dashboard.test.js.snap b/public/pages/Dashboard/containers/__snapshots__/Dashboard.test.js.snap index 0476105c2..c35a36524 100644 --- a/public/pages/Dashboard/containers/__snapshots__/Dashboard.test.js.snap +++ b/public/pages/Dashboard/containers/__snapshots__/Dashboard.test.js.snap @@ -760,6 +760,7 @@ exports[`Dashboard renders in flyout 1`] = ` }, ] } + data-test-subj="alertsDashboard_table" isSelectable={true} itemId={[Function]} items={Array []} @@ -798,6 +799,7 @@ exports[`Dashboard renders in flyout 1`] = ` >
@@ -2497,6 +2499,7 @@ exports[`Dashboard renders with alert by triggers view 1`] = ` }, ] } + data-test-subj="alertsDashboard_table" isSelectable={true} itemId={[Function]} items={Array []} @@ -2522,6 +2525,7 @@ exports[`Dashboard renders with alert by triggers view 1`] = ` >
@@ -4404,6 +4408,7 @@ exports[`Dashboard renders with per alert view 1`] = ` }, ] } + data-test-subj="alertsDashboard_table" isSelectable={true} itemId={[Function]} items={Array []} @@ -4442,6 +4447,7 @@ exports[`Dashboard renders with per alert view 1`] = ` >