From 5e357a5f19dd2cc43e8726582ff66946a3149f6d Mon Sep 17 00:00:00 2001 From: AWSHurneyt <79280347+AWSHurneyt@users.noreply.github.com> Date: Thu, 27 Jan 2022 11:20:37 -0800 Subject: [PATCH] Implemented a toast to display successful attempts to acknowledge alerts. Refactored alerts dashboard flyout to refresh its alerts table when alerts are acknowledged. (#160) * Implemented unit and integ tests for the alerts dashboard flyout. Refactored AlertsDashboardFlyoutComponent::getBucketLevelGraphConditions to return a string with line breaks instead of an array of HTML elements. Signed-off-by: AWSHurneyt Signed-off-by: AWSHurneyt * Removed an unused test variable. Signed-off-by: AWSHurneyt * Removed debug logs. Signed-off-by: AWSHurneyt * Implemented unit test. Refactored integration tests to use fewer wait periods. Signed-off-by: AWSHurneyt * Examining flakiness in cypress test. Signed-off-by: AWSHurneyt * Added short wait period to flyout cypress tests to alleviate flakiness. Signed-off-by: AWSHurneyt * Refactored flyout cypress tests to use aliases. Signed-off-by: AWSHurneyt --- .../pages/Dashboard/containers/Dashboard.js | 178 ++++++++---------- 1 file changed, 81 insertions(+), 97 deletions(-) diff --git a/public/pages/Dashboard/containers/Dashboard.js b/public/pages/Dashboard/containers/Dashboard.js index bf747ad86..444f2994f 100644 --- a/public/pages/Dashboard/containers/Dashboard.js +++ b/public/pages/Dashboard/containers/Dashboard.js @@ -32,16 +32,23 @@ import ContentPanel from '../../../components/ContentPanel'; import DashboardEmptyPrompt from '../components/DashboardEmptyPrompt'; import DashboardControls from '../components/DashboardControls'; import { alertColumns, queryColumns } from '../utils/tableUtils'; -import { MONITOR_TYPE, OPENSEARCH_DASHBOARDS_AD_PLUGIN } from '../../../utils/constants'; +import { + ALERT_STATE, + MONITOR_TYPE, + OPENSEARCH_DASHBOARDS_AD_PLUGIN, +} from '../../../utils/constants'; import { backendErrorNotification } from '../../../utils/helpers'; import { + displayAcknowledgedAlertsToast, + filterActiveAlerts, getInitialSize, + getQueryObjectFromState, + getURLQueryParams, groupAlertsByTrigger, insertGroupByColumn, - removeColumns, } from '../utils/helpers'; import { DEFAULT_PAGE_SIZE_OPTIONS } from '../../Monitors/containers/Monitors/utils/constants'; -import { MAX_ALERT_COUNT, DEFAULT_GET_ALERTS_QUERY_PARAMS } from '../utils/constants'; +import { MAX_ALERT_COUNT } from '../utils/constants'; // TODO: Abstract out a Table component to be used in both Dashboard and Monitors @@ -49,7 +56,7 @@ export default class Dashboard extends Component { constructor(props) { super(props); - const { flyoutAlerts, isAlertsFlyout = false, perAlertView } = props; + const { location, perAlertView } = props; const { alertState, @@ -59,12 +66,13 @@ export default class Dashboard extends Component { size, sortDirection, sortField, - } = this.getURLQueryParams(); + } = getURLQueryParams(location); this.state = { alerts: [], alertsByTriggers: [], alertState, + flyoutIsOpen: false, loadingMonitors: true, monitors: [], monitorIds: this.props.monitorIds, @@ -72,12 +80,11 @@ export default class Dashboard extends Component { search, selectedItems: [], severityLevel, - size: getInitialSize(isAlertsFlyout, perAlertView, size), + size: getInitialSize(perAlertView, size), sortDirection, sortField, totalAlerts: 0, totalTriggers: 0, - trimmedFlyoutAlerts: flyoutAlerts ? flyoutAlerts.slice(0, 10) : [], }; } @@ -110,8 +117,8 @@ export default class Dashboard extends Component { } componentDidUpdate(prevProps, prevState) { - const prevQuery = this.getQueryObjectFromState(prevState); - const currQuery = this.getQueryObjectFromState(this.state); + const prevQuery = getQueryObjectFromState(prevState); + const currQuery = getQueryObjectFromState(this.state); if (!_.isEqual(prevQuery, currQuery)) { const { page, @@ -136,50 +143,6 @@ export default class Dashboard extends Component { } } - getQueryObjectFromState = ({ - page, - size, - search, - sortField, - sortDirection, - severityLevel, - alertState, - monitorIds, - }) => ({ - page, - size, - search, - sortField, - sortDirection, - severityLevel, - alertState, - monitorIds, - }); - - getURLQueryParams = () => { - const { - from = DEFAULT_GET_ALERTS_QUERY_PARAMS.from, - size = DEFAULT_GET_ALERTS_QUERY_PARAMS.size, - search = DEFAULT_GET_ALERTS_QUERY_PARAMS.search, - sortField = DEFAULT_GET_ALERTS_QUERY_PARAMS.sortField, - sortDirection = DEFAULT_GET_ALERTS_QUERY_PARAMS.sortDirection, - severityLevel = DEFAULT_GET_ALERTS_QUERY_PARAMS.severityLevel, - alertState = DEFAULT_GET_ALERTS_QUERY_PARAMS.alertState, - monitorIds = this.props.monitorIds, - } = queryString.parse(this.props.location.search); - - return { - from: isNaN(parseInt(from, 10)) ? DEFAULT_GET_ALERTS_QUERY_PARAMS.from : parseInt(from, 10), - size: isNaN(parseInt(size, 10)) ? DEFAULT_GET_ALERTS_QUERY_PARAMS.size : parseInt(size, 10), - search, - sortField, - sortDirection, - severityLevel, - alertState, - monitorIds, - }; - }; - getAlerts = _.debounce( (from, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds) => { const params = { @@ -259,7 +222,8 @@ export default class Dashboard extends Component { if (!selectedItems.length) return; - const selectedAlerts = perAlertView ? selectedItems : _.get(selectedItems, '0.alerts', []); + let selectedAlerts = perAlertView ? selectedItems : _.get(selectedItems, '0.alerts', []); + selectedAlerts = filterActiveAlerts(selectedAlerts); const monitorAlerts = selectedAlerts.reduce((monitorAlerts, alert) => { const { id, monitor_id: monitorId } = alert; @@ -268,7 +232,7 @@ export default class Dashboard extends Component { return monitorAlerts; }, {}); - const promises = Object.entries(monitorAlerts).map(([monitorId, alerts]) => + Object.entries(monitorAlerts).map(([monitorId, alerts]) => httpClient .post(`../api/alerting/monitors/${monitorId}/_acknowledge/alerts`, { body: JSON.stringify({ alerts }), @@ -276,14 +240,14 @@ export default class Dashboard extends Component { .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 values = await Promise.all(promises); - console.log('values:', values); - // // TODO: Show which values failed, succeeded, etc. const { page, size, @@ -308,23 +272,9 @@ export default class Dashboard extends Component { }; onTableChange = ({ page: tablePage = {}, sort = {} }) => { - const { isAlertsFlyout } = this.props; const { index: page, size } = tablePage; - const { field: sortField, direction: sortDirection } = sort; - this.setState({ - page, - size, - sortField, - sortDirection, - }); - - // If the table is in flyout, return the trimmed array of alerts. - if (isAlertsFlyout) { - const { flyoutAlerts } = this.props; - const trimmedFlyoutAlerts = flyoutAlerts.slice(page * size, page * size + size); - this.setState({ trimmedFlyoutAlerts }); - } + this.setState({ page, size, sortField, sortDirection }); }; onSeverityLevelChange = (e) => { @@ -347,6 +297,42 @@ export default class Dashboard extends Component { this.setState({ page }); }; + openFlyout = (payload) => { + this.setState({ ...this.state, flyoutIsOpen: true }); + this.props.setFlyout({ + type: 'alertsDashboard', + payload: { ...payload }, + }); + }; + + closeFlyout = () => { + this.props.setFlyout(null); + this.setState({ ...this.state, flyoutIsOpen: false }); + }; + + refreshDashboard = () => { + const { + page, + size, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds, + } = this.state; + this.getAlerts( + page * size, + size, + search, + sortField, + sortDirection, + severityLevel, + alertState, + monitorIds + ); + }; + render() { const { alerts, @@ -363,7 +349,6 @@ export default class Dashboard extends Component { sortField, totalAlerts, totalTriggers, - trimmedFlyoutAlerts, } = this.state; const { monitorIds, @@ -393,14 +378,12 @@ export default class Dashboard extends Component { location, monitors, notifications, - setFlyout + setFlyout, + this.openFlyout, + this.closeFlyout, + this.refreshDashboard ); - if (isAlertsFlyout) { - totalItems = this.props.flyoutAlerts.length; - columnType = removeColumns(['severity', 'trigger_name'], columnType); - } - const pagination = { pageIndex: page, pageSize: size, @@ -417,11 +400,13 @@ export default class Dashboard extends Component { const selection = { onSelectionChange: this.onSelectionChange, - selectable: perAlertView ? (item) => item.state === 'ACTIVE' : (item) => item.ACTIVE > 0, + selectable: perAlertView + ? (item) => item.state === ALERT_STATE.ACTIVE + : (item) => item.ACTIVE > 0, selectableMessage: perAlertView - ? (selectable) => (selectable ? undefined : 'Only Active Alerts are Acknowledgeable') + ? (selectable) => (selectable ? undefined : 'Only active alerts can be acknowledged.') : (selectable) => - selectable ? undefined : 'Only Triggers with Active Alerts are Acknowledgeable', + selectable ? undefined : 'Only triggers with active alerts can be acknowledged.', }; const actions = () => { @@ -430,7 +415,7 @@ export default class Dashboard extends Component { Acknowledge , @@ -441,18 +426,17 @@ export default class Dashboard extends Component { actions.unshift( { - setFlyout({ - type: 'alertsDashboard', - payload: { - ...alert, - history, - httpClient, - loadingMonitors, - location, - monitors, - notifications, - setFlyout, - }, + this.openFlyout({ + ...alert, + history, + httpClient, + loadingMonitors, + location, + monitors, + notifications, + setFlyout, + closeFlyout: this.closeFlyout, + refreshDashboard: this.refreshDashboard, }); }} disabled={selectedItems.length !== 1} @@ -503,7 +487,7 @@ export default class Dashboard extends Component {