diff --git a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js index 7c902d97e..e95a89d9f 100644 --- a/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js +++ b/public/components/Flyout/flyouts/components/AlertsDashboardFlyoutComponent.js @@ -33,7 +33,7 @@ import { import { TRIGGER_TYPE } from '../../../../pages/CreateTrigger/containers/CreateTrigger/utils/constants'; import { UNITS_OF_TIME } from '../../../../pages/CreateMonitor/components/MonitorExpressions/expressions/utils/constants'; import { DEFAULT_WHERE_EXPRESSION_TEXT } from '../../../../pages/CreateMonitor/components/MonitorExpressions/expressions/utils/whereHelpers'; -import { backendErrorNotification } from '../../../../utils/helpers'; +import { acknowledgeAlerts, backendErrorNotification } from '../../../../utils/helpers'; import { displayAcknowledgedAlertsToast, filterActiveAlerts, @@ -132,7 +132,7 @@ export default class AlertsDashboardFlyoutComponent extends Component { } const { monitorType } = this.state; if ( - monitorType === MONITOR_TYPE.DOC_LEVEL && + [MONITOR_TYPE.DOC_LEVEL, MONITOR_TYPE.COMPOSITE_LEVEL].includes(monitorType) && !_.isEqual(prevState.selectedItems, this.state.selectedItems) ) this.setState({ tabContent: this.renderAlertsTable() }); @@ -213,34 +213,10 @@ export default class AlertsDashboardFlyoutComponent extends Component { 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 { httpClient, notifications } = this.props; + acknowledgeAlerts(httpClient, notifications, selectedItems); const { page, size, search, sortField, sortDirection, severityLevel, alertState, monitorIds } = this.state; @@ -330,6 +306,7 @@ export default class AlertsDashboardFlyoutComponent extends Component { case MONITOR_TYPE.QUERY_LEVEL: case MONITOR_TYPE.CLUSTER_METRICS: case MONITOR_TYPE.DOC_LEVEL: + case MONITOR_TYPE.COMPOSITE_LEVEL: return `${item.id}-${item.version}`; case MONITOR_TYPE.BUCKET_LEVEL: return item.id; @@ -570,6 +547,10 @@ export default class AlertsDashboardFlyoutComponent extends Component { displayTableTabs = false; break; } + const monitorUrl = `${PLUGIN_NAME}#/monitors/${monitor_id}${ + monitorType === MONITOR_TYPE.COMPOSITE_LEVEL ? '?type=workflow' : '' + }`; + return (
@@ -614,7 +595,7 @@ export default class AlertsDashboardFlyoutComponent extends Component { Monitor

- {monitor_name} + {monitor_name}

@@ -632,7 +613,7 @@ export default class AlertsDashboardFlyoutComponent extends Component { - {monitorType !== MONITOR_TYPE.DOC_LEVEL && ( + {![MONITOR_TYPE.DOC_LEVEL, MONITOR_TYPE.COMPOSITE_LEVEL].includes(monitorType) && ( - {monitorType !== MONITOR_TYPE.DOC_LEVEL && ( + {![MONITOR_TYPE.DOC_LEVEL, MONITOR_TYPE.COMPOSITE_LEVEL].includes(monitorType) && (
diff --git a/public/components/FormControls/FormikCheckableCard/FormikCheckableCard.js b/public/components/FormControls/FormikCheckableCard/FormikCheckableCard.js index e8e6382aa..037924aa3 100644 --- a/public/components/FormControls/FormikCheckableCard/FormikCheckableCard.js +++ b/public/components/FormControls/FormikCheckableCard/FormikCheckableCard.js @@ -6,29 +6,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import { EuiCheckableCard } from '@elastic/eui'; - import FormikInputWrapper from '../FormikInputWrapper'; -import FormikFormRow from '../FormikFormRow'; -const FormikCheckableCard = ({ - name, - formRow = false, - fieldProps = {}, - rowProps = {}, - inputProps = {}, -}) => ( +const FormikCheckableCard = ({ name, fieldProps = {}, inputProps = {} }) => ( - formRow ? ( - - - - ) : ( - - ) - } + render={({ field, form }) => ( + + )} /> ); @@ -47,14 +33,13 @@ const FieldCheckableCard = ({ onChange={(e) => typeof onChange === 'function' ? onChange(e, field, form) : field.onChange(e) } + className="eui-fullHeight" /> ); FormikCheckableCard.propTypes = { name: PropTypes.string.isRequired, - formRow: PropTypes.bool, fieldProps: PropTypes.object, - rowProps: PropTypes.object, inputProps: PropTypes.object, }; diff --git a/public/components/FormControls/FormikCheckableCard/__snapshots__/FormikCheckableCard.test.js.snap b/public/components/FormControls/FormikCheckableCard/__snapshots__/FormikCheckableCard.test.js.snap index 303b528a6..9375a7b4e 100644 --- a/public/components/FormControls/FormikCheckableCard/__snapshots__/FormikCheckableCard.test.js.snap +++ b/public/components/FormControls/FormikCheckableCard/__snapshots__/FormikCheckableCard.test.js.snap @@ -2,7 +2,7 @@ exports[`FormikCheckableCard render 1`] = `
+
-
- -
-
-
-
-
+ class="euiRadio__circle" + />
+
+
`; diff --git a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js index 2981d0afd..27e6e6d7b 100644 --- a/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js +++ b/public/pages/CreateMonitor/components/AssociateMonitors/AssociateMonitors.js @@ -49,8 +49,9 @@ const AssociateMonitors = ({ isDarkMode, values, httpClient, errors }) => {

Delegate monitors

- Delegate two or more monitors to run as part of this workflow. The monitor types per query, - per bucket, and per document are supported.{' '} + Delegate two or more monitors to run as part of this workflow. The order in which you select + the monitors determines their order in the workflow. The monitor types per query, per + bucket, and per document are supported.  - Delegate two or more monitors to run as part of this workflow. The monitor types per query, per bucket, and per document are supported. + Delegate two or more monitors to run as part of this workflow. The order in which you select the monitors determines their order in the workflow. The monitor types per query, per bucket, and per document are supported. 
+
-
- -
-
-
-
- -
+ class="euiRadio__circle" + />
+
+ +
+
-
- -
-
-
-
- -
+ class="euiRadio__circle" + />
+
+ +
@@ -181,46 +163,37 @@ exports[`MonitorDefinitionCard renders without AD plugin 2`] = ` style="width:275px" >
+
-
- -
-
-
-
- -
+ class="euiRadio__circle" + />
+
+ +
+
-
- -
-
-
-
- -
+ class="euiRadio__circle" + />
+
+ +
diff --git a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js index bf1442bbe..109540acb 100644 --- a/public/pages/CreateMonitor/components/MonitorType/MonitorType.js +++ b/public/pages/CreateMonitor/components/MonitorType/MonitorType.js @@ -4,7 +4,15 @@ */ import React from 'react'; -import { EuiFlexGrid, EuiFlexItem, EuiText } from '@elastic/eui'; +import { + EuiFlexGrid, + EuiFlexGroup, + EuiFlexItem, + EuiFormLabel, + EuiFormRow, + EuiSpacer, + EuiText, +} from '@elastic/eui'; import FormikCheckableCard from '../../../../components/FormControls/FormikCheckableCard'; import { MONITOR_TYPE, SEARCH_TYPE } from '../../../../utils/constants'; import { FORMIK_INITIAL_TRIGGER_VALUES } from '../../../CreateTrigger/containers/CreateTrigger/utils/constants'; @@ -75,88 +83,82 @@ const compositeLevelDescription = ( ); const MonitorType = ({ values }) => ( - - - onChangeDefinition(e, form), - children: queryLevelDescription, - 'data-test-subj': 'queryLevelMonitorRadioCard', - }} - /> - - - onChangeDefinition(e, form), - children: bucketLevelDescription, - 'data-test-subj': 'bucketLevelMonitorRadioCard', - }} - /> - - - onChangeDefinition(e, form), - children: clusterMetricsDescription, - 'data-test-subj': 'clusterMetricsMonitorRadioCard', - }} - /> - - - onChangeDefinition(e, form), - children: documentLevelDescription, - 'data-test-subj': 'docLevelMonitorRadioCard', - }} - /> - - - onChangeDefinition(e, form), - children: compositeLevelDescription, - 'data-test-subj': 'compositeLevelMonitorRadioCard', - }} - /> - - + <> + Monitor type + + + + onChangeDefinition(e, form), + children: queryLevelDescription, + 'data-test-subj': 'queryLevelMonitorRadioCard', + }} + /> + + + onChangeDefinition(e, form), + children: bucketLevelDescription, + 'data-test-subj': 'bucketLevelMonitorRadioCard', + }} + /> + + + onChangeDefinition(e, form), + children: clusterMetricsDescription, + 'data-test-subj': 'clusterMetricsMonitorRadioCard', + }} + /> + + + onChangeDefinition(e, form), + children: documentLevelDescription, + 'data-test-subj': 'docLevelMonitorRadioCard', + }} + /> + + + onChangeDefinition(e, form), + children: compositeLevelDescription, + 'data-test-subj': 'compositeLevelMonitorRadioCard', + }} + /> + + + ); export default MonitorType; diff --git a/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap b/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap index 8c5ea8fbf..a464e778f 100644 --- a/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap +++ b/public/pages/CreateMonitor/components/MonitorType/__snapshots__/MonitorType.test.js.snap @@ -1,328 +1,288 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`MonitorType renders 1`] = ` -
+Array [ + , +
,
- -
-
+
- -
-
+ class="euiRadio__circle" + />
+
+
+
-
-
- Per query monitors run a query and generate alerts based on trigger criteria that match query results. -
+ Per query monitors run a query and generate alerts based on trigger criteria that match query results.
-
-
+
- -
-
+ class="euiRadio__circle" + />
+
+
+
-
-
- Per bucket monitors run a query that evaluates trigger criteria based on aggregated values in the dataset. -
+ Per bucket monitors run a query that evaluates trigger criteria based on aggregated values in the dataset.
-
-
+
- -
-
+ class="euiRadio__circle" + />
+
+
+
-
-
- Per cluster metrics monitors run API requests to monitor the cluster’s health. -
+ Per cluster metrics monitors run API requests to monitor the cluster’s health.
-
-
+
- -
-
+ class="euiRadio__circle" + />
+
+
+
-
-
- Per document monitors run queries that return individual documents matching the trigger conditions. -
+ Per document monitors run queries that return individual documents matching the trigger conditions.
-
-
+
- -
-
+ class="euiRadio__circle" + />
+
+
+
-
-
- Composite monitors chain the outputs of different monitor types and focus trigger conditions to reduce alert noise. -
+ Composite monitors chain the outputs of different monitor types and focus trigger conditions to reduce alert noise.
-
-
+
, +] `; diff --git a/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap index 5c36ca0fb..967b66545 100644 --- a/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap +++ b/public/pages/CreateMonitor/containers/WorkflowDetails/__snapshots__/WorkflowDetails.test.js.snap @@ -263,7 +263,7 @@ exports[`WorkflowDetails renders 1`] = `
- Delegate two or more monitors to run as part of this workflow. The monitor types per query, per bucket, and per document are supported. + Delegate two or more monitors to run as part of this workflow. The order in which you select the monitors determines their order in the workflow. The monitor types per query, per bucket, and per document are supported.  { const expressions = _.cloneDeep(usedExpressions); - expressions[idx] = { ...expressions[idx], description: selection[0].description }; + expressions[idx] = { ...expressions[idx], description: selection }; setUsedExpressions(expressions); onChange(form, expressions); }; @@ -244,21 +245,16 @@ const ExpressionBuilder = ({ data-test-subj={`${formikFullFieldName}_${triggerIndex}_${idx}_options`} > - changeCondition(selection, expression, idx, form)} + style={{ width: '100px' }} + data-test-subj={`condition-select-${triggerIndex}-${idx}`} + value={expression.description} + onChange={(event) => changeCondition(event.target.value, expression, idx, form)} + options={(idx === 0 ? FIRST_EXPRESSION_CONDITIONS_MAP : EXPRESSION_CONDITIONS_MAP).map( + (opt) => ({ value: opt.description, text: opt.label }) + )} onBlur={() => onBlur(form, usedExpressions)} - options={idx === 0 ? FIRST_EXPRESSION_CONDITIONS_MAP : EXPRESSION_CONDITIONS_MAP} - autoFocus={false} /> {renderMonitorOptions(expression, idx, form)} diff --git a/public/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js b/public/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js index 716a51dc0..8da64c325 100644 --- a/public/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js +++ b/public/pages/CreateTrigger/containers/ConfigureActions/ConfigureActions.js @@ -378,7 +378,7 @@ class ConfigureActions extends React.Component { hasShadow={!flyoutMode} hasBorder={!flyoutMode} > - {!flyoutMode && !loadingDestinations && numActions < 1 ? ( + {!flyoutMode && loadingDestinations && numActions < 1 ? (
Loading Destinations...
) : ( <> diff --git a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js index 5e6305b98..79457e5df 100644 --- a/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js +++ b/public/pages/CreateTrigger/containers/DefineCompositeLevelTrigger/TriggerNotifications.js @@ -105,35 +105,38 @@ const TriggerNotifications = ({ {actions.length ? actions.map((action, actionIndex) => ( - {`Notification ${actionIndex + 1}`}} - paddingSize={'s'} - extraAction={ - onRemoveNotification(actionIndex)} - size={'s'} + <> + {actionIndex > 0 && } + {`Notification ${actionIndex + 1}`}} + paddingSize={'s'} + extraAction={ + onRemoveNotification(actionIndex)} + size={'s'} + /> + } + > + - } - > - - + + )) : null} {actions.length ? : null} diff --git a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx index f50108e75..b5fd8d2b2 100644 --- a/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx +++ b/public/pages/Dashboard/components/ChainedAlertDetailsFlyout/ChainedAlertDetails.tsx @@ -3,17 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect, useState } from 'react'; +import React, { useState } from 'react'; import { EuiFlexGrid, EuiTitle, EuiSpacer, EuiBasicTableColumn, - EuiButtonIcon, - EuiPanel, - EuiFlexGroup, - EuiFlexItem, - EuiText, EuiInMemoryTable } from '@elastic/eui'; import OverviewStat from '../../../MonitorDetails/components/OverviewStat'; @@ -26,52 +21,6 @@ export const ChainedAlertDetails = ({ alert, associatedAlerts }) => { [key: string]: JSX.Element; }>({}); - const toggleCorrelationDetails = (item: any) => { - const itemIdToExpandedRowMapValues = { ...itemIdToExpandedRowMap }; - if (itemIdToExpandedRowMapValues[item.id]) { - delete itemIdToExpandedRowMapValues[item.id]; - } else { - itemIdToExpandedRowMapValues[item.id] = ( - - - - State - - - - {typeof item.state !== 'string' ? DEFAULT_EMPTY_DATA : _.capitalize(item.state.toLowerCase())} - - - - - - - End time - - - - {item.end_time || DEFAULT_EMPTY_DATA} - - - - - - - Time acknowledged - - - - {item.acknowledged_time || DEFAULT_EMPTY_DATA} - - - - - ); - } - - setItemIdToExpandedRowMap(itemIdToExpandedRowMapValues); - }; - const overviewItems = [ { header: 'Trigger name', @@ -91,19 +40,6 @@ export const ChainedAlertDetails = ({ alert, associatedAlerts }) => { } ]; - const actions: any[] = [ - { - render: (item: any) => ( - toggleCorrelationDetails(item)} - aria-label={itemIdToExpandedRowMap[item.id] ? 'Collapse' : 'Expand'} - iconType={itemIdToExpandedRowMap[item.id] ? 'arrowUp' : 'arrowDown'} - /> - ), - width: '50px' - }, - ]; - return ( <> @@ -117,11 +53,10 @@ export const ChainedAlertDetails = ({ alert, associatedAlerts }) => { []} + columns={associatedAlertsTableColumns as EuiBasicTableColumn[]} items={associatedAlerts} itemId='id' itemIdToExpandedRowMap={itemIdToExpandedRowMap} - hasActions={true} isExpandable={true} pagination={true} sorting={true} diff --git a/public/pages/Dashboard/containers/Dashboard.js b/public/pages/Dashboard/containers/Dashboard.js index 874b1729b..cea536244 100644 --- a/public/pages/Dashboard/containers/Dashboard.js +++ b/public/pages/Dashboard/containers/Dashboard.js @@ -25,7 +25,7 @@ import { MONITOR_TYPE, OPENSEARCH_DASHBOARDS_AD_PLUGIN, } from '../../../utils/constants'; -import { backendErrorNotification } from '../../../utils/helpers'; +import { acknowledgeAlerts, backendErrorNotification } from '../../../utils/helpers'; import { displayAcknowledgedAlertsToast, filterActiveAlerts, @@ -194,35 +194,7 @@ export default class Dashboard extends Component { acknowledgeAlerts = async (alerts) => { const { httpClient, notifications } = this.props; - const selectedAlerts = filterActiveAlerts(alerts); - - const monitorAlerts = selectedAlerts.reduce((monitorAlerts, alert) => { - const id = alert.id; - const monitorId = alert.workflow_id || alert.monitor_id; - if (monitorAlerts[monitorId]) monitorAlerts[monitorId].alerts.push(id); - else - monitorAlerts[monitorId] = { - alerts: [id], - poolType: !!alert.workflow_id ? 'workflows' : 'monitors', - }; - return monitorAlerts; - }, {}); - - Object.entries(monitorAlerts).map(([monitorId, { alerts, poolType }]) => - httpClient - .post(`../api/alerting/${poolType}/${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) - ); + await Promise.all(acknowledgeAlerts(httpClient, notifications, alerts)); }; // TODO: exists in both Dashboard and Monitors, should be moved to redux when implemented diff --git a/public/pages/Dashboard/utils/tableUtils.js b/public/pages/Dashboard/utils/tableUtils.js index f60d37a33..58c56a08b 100644 --- a/public/pages/Dashboard/utils/tableUtils.js +++ b/public/pages/Dashboard/utils/tableUtils.js @@ -251,4 +251,10 @@ export const associatedAlertsTableColumns = [ truncateText: true, textOnly: true, }, + { + field: 'state', + name: 'State', + truncateText: true, + textOnly: true, + }, ]; diff --git a/public/utils/helpers.js b/public/utils/helpers.js index 31a87d577..36e355510 100644 --- a/public/utils/helpers.js +++ b/public/utils/helpers.js @@ -6,6 +6,11 @@ import React from 'react'; import { EuiText } from '@elastic/eui'; import { htmlIdGenerator } from '@elastic/eui/lib/services'; +import { + displayAcknowledgedAlertsToast, + filterActiveAlerts, +} from '../pages/Dashboard/utils/helpers'; +import _ from 'lodash'; export const makeId = htmlIdGenerator(); @@ -83,3 +88,38 @@ export const getUniqueName = (values, prefix) => { return getUniqueName(lastDigit); }; + +export async function acknowledgeAlerts(httpClient, notifications, alerts) { + const selectedAlerts = filterActiveAlerts(alerts); + + const monitorAlerts = selectedAlerts.reduce((monitorAlerts, alert) => { + const id = alert.id; + const monitorId = alert.workflow_id || alert.monitor_id; + if (monitorAlerts[monitorId]) monitorAlerts[monitorId].alerts.push(id); + else + monitorAlerts[monitorId] = { + alerts: [id], + poolType: !!alert.workflow_id ? 'workflows' : 'monitors', + }; + return monitorAlerts; + }, {}); + + const acknowledgePromises = Object.entries(monitorAlerts).map( + ([monitorId, { alerts, poolType }]) => + httpClient + .post(`../api/alerting/${poolType}/${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) + ); + + return acknowledgePromises; +}