From 7691ea669a17be17087c0ed701d0959455eb73c1 Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Mon, 28 Nov 2022 18:06:23 -0600 Subject: [PATCH 01/12] set up charts panel and placehoders --- .../components/severity/severity_donut.tsx | 63 ++++++++ .../alerts_kpis/alerts_charts_panel/index.tsx | 144 ++++++++++++++++++ .../alerts_charts_panel/translations.ts | 15 ++ .../chart_panels/chart_select/helpers.ts | 18 ++- .../chart_panels/chart_select/index.tsx | 14 +- .../chart_panels/chart_select/translations.ts | 4 + .../detection_engine/chart_panels/index.tsx | 33 +++- 7 files changed, 281 insertions(+), 10 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts diff --git a/x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx b/x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx new file mode 100644 index 0000000000000..1245ab36810fc --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import type { ShapeTreeNode } from '@elastic/charts'; +import styled from 'styled-components'; + +import type { SeverityCount } from './types'; +import { useRiskDonutChartData } from './use_risk_donut_chart_data'; +import type { FillColor } from './charts/donutchart'; +import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; +import { RISK_SEVERITY_COLOUR } from '../../../../common/components/severity/common'; +import { DonutChart } from '../../../../common/components/charts/donutchart'; +import { Legend } from '../../../../common/components/charts/legend'; +import { ChartLabel } from '../../detection_response/alerts_by_status/chart_label'; +import * as i18n from './translations'; +import type { RiskSeverity } from '../../../../../common/search_strategy'; + +const DONUT_HEIGHT = 120; + +const fillColor: FillColor = (d: ShapeTreeNode) => { + return RISK_SEVERITY_COLOUR[d.dataName as RiskSeverity] ?? emptyDonutColor; +}; + +const DonutContainer = styled(EuiFlexItem)` + padding-right: ${({ theme }) => theme.eui.euiSizeXXL}; + padding-left: ${({ theme }) => theme.eui.euiSizeM}; +`; + +const StyledLegendItems = styled(EuiFlexItem)` + justify-content: center; +`; + +interface RiskScoreDonutChartProps { + severityCount: SeverityCount; +} + +export const RiskScoreDonutChart = ({ severityCount }: RiskScoreDonutChartProps) => { + const [donutChartData, legendItems, total] = useRiskDonutChartData(severityCount); + + return ( + + + {legendItems.length > 0 && } + + + } + totalCount={total} + /> + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx new file mode 100644 index 0000000000000..a5ad6feeee3be --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx @@ -0,0 +1,144 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; +import React, { memo, useMemo, useCallback } from 'react'; +// import React, { memo, useMemo, useState, useEffect, useCallback } from 'react'; +import uuid from 'uuid'; +import styled from 'styled-components'; +import type { Filter, Query } from '@kbn/es-query'; +// import { buildEsQuery } from '@kbn/es-query'; + +// import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { InspectButtonContainer } from '../../../../common/components/inspect'; +import * as i18n from './translations'; +import { KpiPanel } from '../common/components'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +// import { PlaceHolder } from './placeholder'; +// import { RiskScoreDonutChart } from '../../../../overview/components/entity_analytics/common/risk_score_donut_chart'; +export const DETECTIONS_ALERTS_CHARTS_ID = 'detections-alerts-charts'; + +const PlaceHolder = () => { + return ( + + + +

{'placeholder'}

+
+
+
+ ); +}; + +interface AlertsChartsPanelProps { + alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; + chartOptionsContextMenu?: (queryId: string) => React.ReactNode; + filters?: Filter[]; + inspectTitle: string; + panelHeight?: number; + query?: Query; + signalIndexName: string | null; + title?: React.ReactNode; + runtimeMappings?: MappingRuntimeFields; +} + +export const AlertsChartsPanel = memo( + ({ + alignHeader, + chartOptionsContextMenu, + filters, + inspectTitle, + panelHeight, + query, + runtimeMappings, + signalIndexName, + title = i18n.CHARTS_TITLE, + }) => { + // const { to, from, deleteQuery, setQuery } = useGlobalTime(false); + + // create a unique, but stable (across re-renders) query id + const uniqueQueryId = useMemo(() => `${DETECTIONS_ALERTS_CHARTS_ID}-${uuid.v4()}`, []); + + // const additionalFilters = useMemo(() => { + // try { + // return [ + // buildEsQuery( + // undefined, + // query != null ? [query] : [], + // filters?.filter((f) => f.meta.disabled === false) ?? [] + // ), + // ]; + // } catch (e) { + // return []; + // } + // }, [query, filters]); + + const { toggleStatus, setToggleStatus } = useQueryToggle(DETECTIONS_ALERTS_CHARTS_ID); + // const [querySkip, setQuerySkip] = useState(!toggleStatus); + // useEffect(() => { + // setQuerySkip(!toggleStatus); + // }, [toggleStatus]); + const toggleQuery = useCallback( + (status: boolean) => { + setToggleStatus(status); + // toggle on = skipQuery false + // setQuerySkip(!status); + }, + [setToggleStatus] + // [setQuerySkip, setToggleStatus] + ); + + const ChartOptionsFlexItem = styled(EuiFlexItem)` + margin-left: ${({ theme }) => theme.eui.euiSizeS}; + `; + + return ( + + + + {/* */} + {/* */} + {chartOptionsContextMenu != null && ( + + {chartOptionsContextMenu(uniqueQueryId)} + + )} + {/* */} + {/* */} + + {toggleStatus && ( + + + + + + )} + + + ); + } +); + +AlertsChartsPanel.displayName = 'AlertsChartsPanel'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts new file mode 100644 index 0000000000000..124d0beb85a29 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CHARTS_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.alerts.charts.chartsTitle', + { + defaultMessage: 'Charts', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts index 08507759b375e..9166e73331413 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts @@ -12,8 +12,9 @@ import * as i18n from './translations'; export const TABLE_ID = 'table'; export const TREND_ID = 'trend'; export const TREEMAP_ID = 'treemap'; +export const CHARTS_ID = 'charts'; -export type AlertViewSelection = 'trend' | 'table' | 'treemap'; +export type AlertViewSelection = 'trend' | 'table' | 'treemap' | 'charts'; export interface ButtonProperties { 'data-test-subj': string; @@ -35,6 +36,8 @@ export const getButtonProperties = (alertViewSelection: AlertViewSelection): But }; case TREEMAP_ID: return { 'data-test-subj': alertViewSelection, icon: 'grid', name: i18n.TREEMAP }; + case CHARTS_ID: + return { 'data-test-subj': alertViewSelection, icon: 'visPie', name: i18n.CHARTS }; default: return table; } @@ -44,10 +47,12 @@ export const getContextMenuPanels = ({ alertViewSelection, closePopover, setAlertViewSelection, + isAlertsPageChartsEnabled, }: { alertViewSelection: AlertViewSelection; closePopover: () => void; setAlertViewSelection: (alertViewSelection: AlertViewSelection) => void; + isAlertsPageChartsEnabled: boolean; }): EuiContextMenuPanelDescriptor[] => [ { id: 0, @@ -73,6 +78,17 @@ export const getContextMenuPanels = ({ setAlertViewSelection('treemap'); }, }, + ...(isAlertsPageChartsEnabled + ? [ + { + ...getButtonProperties('charts'), + onClick: () => { + closePopover(); + setAlertViewSelection('charts'); + }, + }, + ] + : []), ], }, ]; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx index 5203eaf77edab..307dfb24f0600 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx @@ -13,7 +13,7 @@ import styled from 'styled-components'; import type { AlertViewSelection } from './helpers'; import { getButtonProperties, getContextMenuPanels } from './helpers'; import * as i18n from './translations'; - +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; interface Props { alertViewSelection: AlertViewSelection; setAlertViewSelection: (alertViewSelection: AlertViewSelection) => void; @@ -49,10 +49,16 @@ const ChartSelectComponent: React.FC = ({ ); }, [alertViewSelection, onButtonClick]); - + const isAlertsPageChartsEnabled = useIsExperimentalFeatureEnabled('alertsPageChartsEnabled'); const panels: EuiContextMenuPanelDescriptor[] = useMemo( - () => getContextMenuPanels({ alertViewSelection, closePopover, setAlertViewSelection }), - [alertViewSelection, closePopover, setAlertViewSelection] + () => + getContextMenuPanels({ + alertViewSelection, + closePopover, + setAlertViewSelection, + isAlertsPageChartsEnabled, + }), + [alertViewSelection, closePopover, setAlertViewSelection, isAlertsPageChartsEnabled] ); return ( diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts index e776b94f3f957..a170be5b29720 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts @@ -28,3 +28,7 @@ export const TREEMAP = i18n.translate( defaultMessage: 'Treemap', } ); + +export const CHARTS = i18n.translate('xpack.securitySolution.components.chartSelect.chartsOption', { + defaultMessage: 'Charts', +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx index 14fc97a251309..b9dc4eb7c2ec2 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx @@ -11,15 +11,17 @@ import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { useAlertsLocalStorage } from './alerts_local_storage'; import type { AlertsSettings } from './alerts_local_storage/types'; import { ChartContextMenu } from './chart_context_menu'; import { ChartSelect } from './chart_select'; -import { TABLE, TREEMAP, TREND } from './chart_select/translations'; +import * as i18n from './chart_select/translations'; import { AlertsTreemapPanel } from '../../../../common/components/alerts_treemap_panel'; import type { UpdateDateRange } from '../../../../common/components/charts/common'; import { useEuiComboBoxReset } from '../../../../common/components/use_combo_box_reset'; import { AlertsHistogramPanel } from '../../../components/alerts_kpis/alerts_histogram_panel'; +import { AlertsChartsPanel } from '../../../components/alerts_kpis/alerts_charts_panel'; import { DEFAULT_STACK_BY_FIELD, DEFAULT_STACK_BY_FIELD1, @@ -30,6 +32,7 @@ import { GROUP_BY_LABEL } from '../../../components/alerts_kpis/common/translati const TABLE_PANEL_HEIGHT = 330; // px const TRENT_CHART_HEIGHT = 127; // px const TREND_CHART_PANEL_HEIGHT = 256; // px +const ALERTS_CHARTS_PANEL_HEIGHT = 330; // px const FullHeightFlexItem = styled(EuiFlexItem)` height: 100%; @@ -134,7 +137,7 @@ const ChartPanelsComponent: React.FC = ({ ), [alertViewSelection, setAlertViewSelection] ); - + const isAlertsPageChartsEnabled = useIsExperimentalFeatureEnabled('alertsPageChartsEnabled'); return (
{alertViewSelection === 'trend' && ( @@ -149,7 +152,7 @@ const ChartPanelsComponent: React.FC = ({ comboboxRef={stackByField0ComboboxRef} defaultStackByOption={trendChartStackBy} filters={alertsHistogramDefaultFilters} - inspectTitle={TREND} + inspectTitle={i18n.TREND} setComboboxInputRef={setStackByField0ComboboxInputRef} onFieldSelected={updateCommonStackBy0} panelHeight={TREND_CHART_PANEL_HEIGHT} @@ -177,7 +180,7 @@ const ChartPanelsComponent: React.FC = ({ alignHeader="flexStart" chartOptionsContextMenu={chartOptionsContextMenu} filters={alertsHistogramDefaultFilters} - inspectTitle={TABLE} + inspectTitle={i18n.TABLE} panelHeight={TABLE_PANEL_HEIGHT} query={query} runtimeMappings={runtimeMappings} @@ -205,7 +208,7 @@ const ChartPanelsComponent: React.FC = ({ addFilter={addFilter} alignHeader="flexStart" chartOptionsContextMenu={chartOptionsContextMenu} - inspectTitle={TREEMAP} + inspectTitle={i18n.TREEMAP} isPanelExpanded={isTreemapPanelExpanded} filters={alertsHistogramDefaultFilters} query={query} @@ -226,6 +229,26 @@ const ChartPanelsComponent: React.FC = ({ )} )} + + {isAlertsPageChartsEnabled && alertViewSelection === 'charts' && ( + + {isLoadingIndexPattern ? ( + + ) : ( + + )} + + )}
); }; From abafdf17d0185cb85ebc6e0451924ef6fd871e65 Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Mon, 28 Nov 2022 19:24:15 -0600 Subject: [PATCH 02/12] add severity donut structure severity charts draft add inspect and hover actions --- .../common/experimental_features.ts | 5 + .../components/severity/severity_donut.tsx | 63 ------- .../execution_log_table.tsx | 2 +- .../alerts_kpis/alerts_charts_panel/index.tsx | 144 -------------- .../alerts_charts_panel/translations.ts | 15 -- .../alerts_summary_charts_panel/columns.tsx | 54 ++++++ .../alerts_summary_charts_panel/helpers.tsx | 26 +++ .../alerts_summary_charts_panel/index.tsx | 111 +++++++++++ .../severity_level_chart.tsx | 107 +++++++++++ .../translations.ts | 56 ++++++ .../alerts_summary_charts_panel/types.ts | 50 +++++ .../use_severity_chart_data.ts | 175 ++++++++++++++++++ .../detection_engine/chart_panels/index.tsx | 6 +- .../alerts_by_status/alerts_by_status.tsx | 2 +- .../alerts_by_status/use_alerts_by_status.ts | 2 + 15 files changed, 590 insertions(+), 228 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 9a48abc68f466..10a9c651e8e1d 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -80,6 +80,11 @@ export const allowedExperimentalValues = Object.freeze({ * Enables the `get-file` endpoint response action */ responseActionGetFileEnabled: false, + + /** + * Enables top charts on Alerts Page + */ + alertsPageChartsEnabled: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx b/x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx deleted file mode 100644 index 1245ab36810fc..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/severity/severity_donut.tsx +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React from 'react'; -import type { ShapeTreeNode } from '@elastic/charts'; -import styled from 'styled-components'; - -import type { SeverityCount } from './types'; -import { useRiskDonutChartData } from './use_risk_donut_chart_data'; -import type { FillColor } from './charts/donutchart'; -import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; -import { RISK_SEVERITY_COLOUR } from '../../../../common/components/severity/common'; -import { DonutChart } from '../../../../common/components/charts/donutchart'; -import { Legend } from '../../../../common/components/charts/legend'; -import { ChartLabel } from '../../detection_response/alerts_by_status/chart_label'; -import * as i18n from './translations'; -import type { RiskSeverity } from '../../../../../common/search_strategy'; - -const DONUT_HEIGHT = 120; - -const fillColor: FillColor = (d: ShapeTreeNode) => { - return RISK_SEVERITY_COLOUR[d.dataName as RiskSeverity] ?? emptyDonutColor; -}; - -const DonutContainer = styled(EuiFlexItem)` - padding-right: ${({ theme }) => theme.eui.euiSizeXXL}; - padding-left: ${({ theme }) => theme.eui.euiSizeM}; -`; - -const StyledLegendItems = styled(EuiFlexItem)` - justify-content: center; -`; - -interface RiskScoreDonutChartProps { - severityCount: SeverityCount; -} - -export const RiskScoreDonutChart = ({ severityCount }: RiskScoreDonutChartProps) => { - const [donutChartData, legendItems, total] = useRiskDonutChartData(severityCount); - - return ( - - - {legendItems.length > 0 && } - - - } - totalCount={total} - /> - - - ); -}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx index 072d3b6f58174..1a443e79a0d80 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx @@ -475,7 +475,7 @@ const ExecutionLogTableComponent: React.FC = ({ columns={executionLogColumns} items={items} loading={isFetching} - sorting={sorting} + // sorting={sorting} pagination={pagination} onChange={onTableChangeCallback} /> diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx deleted file mode 100644 index a5ad6feeee3be..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/index.tsx +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; -import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; -import React, { memo, useMemo, useCallback } from 'react'; -// import React, { memo, useMemo, useState, useEffect, useCallback } from 'react'; -import uuid from 'uuid'; -import styled from 'styled-components'; -import type { Filter, Query } from '@kbn/es-query'; -// import { buildEsQuery } from '@kbn/es-query'; - -// import { useGlobalTime } from '../../../../common/containers/use_global_time'; -import { HeaderSection } from '../../../../common/components/header_section'; -import { InspectButtonContainer } from '../../../../common/components/inspect'; -import * as i18n from './translations'; -import { KpiPanel } from '../common/components'; -import { useQueryToggle } from '../../../../common/containers/query_toggle'; -// import { PlaceHolder } from './placeholder'; -// import { RiskScoreDonutChart } from '../../../../overview/components/entity_analytics/common/risk_score_donut_chart'; -export const DETECTIONS_ALERTS_CHARTS_ID = 'detections-alerts-charts'; - -const PlaceHolder = () => { - return ( - - - -

{'placeholder'}

-
-
-
- ); -}; - -interface AlertsChartsPanelProps { - alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; - chartOptionsContextMenu?: (queryId: string) => React.ReactNode; - filters?: Filter[]; - inspectTitle: string; - panelHeight?: number; - query?: Query; - signalIndexName: string | null; - title?: React.ReactNode; - runtimeMappings?: MappingRuntimeFields; -} - -export const AlertsChartsPanel = memo( - ({ - alignHeader, - chartOptionsContextMenu, - filters, - inspectTitle, - panelHeight, - query, - runtimeMappings, - signalIndexName, - title = i18n.CHARTS_TITLE, - }) => { - // const { to, from, deleteQuery, setQuery } = useGlobalTime(false); - - // create a unique, but stable (across re-renders) query id - const uniqueQueryId = useMemo(() => `${DETECTIONS_ALERTS_CHARTS_ID}-${uuid.v4()}`, []); - - // const additionalFilters = useMemo(() => { - // try { - // return [ - // buildEsQuery( - // undefined, - // query != null ? [query] : [], - // filters?.filter((f) => f.meta.disabled === false) ?? [] - // ), - // ]; - // } catch (e) { - // return []; - // } - // }, [query, filters]); - - const { toggleStatus, setToggleStatus } = useQueryToggle(DETECTIONS_ALERTS_CHARTS_ID); - // const [querySkip, setQuerySkip] = useState(!toggleStatus); - // useEffect(() => { - // setQuerySkip(!toggleStatus); - // }, [toggleStatus]); - const toggleQuery = useCallback( - (status: boolean) => { - setToggleStatus(status); - // toggle on = skipQuery false - // setQuerySkip(!status); - }, - [setToggleStatus] - // [setQuerySkip, setToggleStatus] - ); - - const ChartOptionsFlexItem = styled(EuiFlexItem)` - margin-left: ${({ theme }) => theme.eui.euiSizeS}; - `; - - return ( - - - - {/* */} - {/* */} - {chartOptionsContextMenu != null && ( - - {chartOptionsContextMenu(uniqueQueryId)} - - )} - {/* */} - {/* */} - - {toggleStatus && ( - - - - - - )} - - - ); - } -); - -AlertsChartsPanel.displayName = 'AlertsChartsPanel'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts deleted file mode 100644 index 124d0beb85a29..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_charts_panel/translations.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export const CHARTS_TITLE = i18n.translate( - 'xpack.securitySolution.detectionEngine.alerts.charts.chartsTitle', - { - defaultMessage: 'Charts', - } -); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx new file mode 100644 index 0000000000000..51f3bdab3ae2a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { EuiHealth, EuiText } from '@elastic/eui'; +import type { EuiBasicTableColumn } from '@elastic/eui'; +import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; +import { capitalize } from 'lodash'; +import { DefaultDraggable } from '../../../../common/components/draggables'; +import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; +import { FormattedCount } from '../../../../common/components/formatted_number'; +import * as i18n from './translations'; + +interface SeverityTableItem { + key: Severity; + value: number; + label: string; +} + +export const getSeverityTableColumns = (): Array> => [ + { + field: 'key', + name: i18n.SEVERITY_LEVEL_COLUMN_TITLE, + 'data-test-subj': 'severityTable-severity', + render: (severity: Severity) => ( + + + + ), + }, + { + field: 'value', + name: i18n.SEVERITY_COUNT_COULMN_TITLE, + sortable: true, + dataType: 'number', + 'data-test-subj': 'severityTable-alertCount', + render: (alertCount: number) => ( + + + + ), + }, +]; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx new file mode 100644 index 0000000000000..bd8b63d203f39 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { chartConfigs } from '../../../../overview/components/detection_response/alerts_by_status/alerts_by_status'; +import type { AlertsBySeverityResponse, AlertsBySeverityAgg, ParsedAlertsData } from './types'; +import * as i18n from './translations'; + +export const parseAlertsData = ( + response: AlertsBySeverityResponse<{}, AlertsBySeverityAgg> +): ParsedAlertsData => { + const severityBuckets = response?.aggregations?.statusBySeverity?.buckets ?? []; + if (severityBuckets.length === 0) { + return null; + } + const data = severityBuckets.map((severity) => { + return { + key: severity.key, + value: severity.doc_count, + label: chartConfigs.find((cfg) => cfg.key === severity.key)?.label ?? i18n.UNKNOWN_SEVERITY, + }; + }); + return data; +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx new file mode 100644 index 0000000000000..065291ab76c25 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; +import React, { useMemo, useCallback, useState, useEffect } from 'react'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; +import type { Filter, Query } from '@kbn/es-query'; +import uuid from 'uuid'; +import * as i18n from './translations'; +import { KpiPanel } from '../common/components'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { useSeverityChartData } from './use_severity_chart_data'; +import { SeverityLevelChart } from './severity_level_chart'; + +const DETECTIONS_ALERTS_CHARTS_ID = 'detections-alerts-charts'; + +const PlaceHolder = ({ title }: { title: string }) => { + return ( + + + +

{title}

+
+
+
+ ); +}; + +interface Props { + alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; + filters?: Filter[]; + panelHeight?: number; + query?: Query; + signalIndexName: string | null; + title?: React.ReactNode; + runtimeMappings?: MappingRuntimeFields; +} + +export const AlertsSummaryChartsPanel: React.FC = ({ + alignHeader, + filters, + panelHeight, + query, + runtimeMappings, + signalIndexName, + title = i18n.CHARTS_TITLE, +}: Props) => { + // create a unique, but stable (across re-renders) query id + const uniqueQueryId = useMemo(() => `${DETECTIONS_ALERTS_CHARTS_ID}-${uuid.v4()}`, []); + + const { toggleStatus, setToggleStatus } = useQueryToggle(DETECTIONS_ALERTS_CHARTS_ID); + const [querySkip, setQuerySkip] = useState(!toggleStatus); + useEffect(() => { + setQuerySkip(!toggleStatus); + }, [toggleStatus]); + const toggleQuery = useCallback( + (status: boolean) => { + setToggleStatus(status); + // toggle on = skipQuery false + setQuerySkip(!status); + }, + [setQuerySkip, setToggleStatus] + ); + + const { items: severityData, isLoading } = useSeverityChartData({ + filters, + query, + signalIndexName, + runtimeMappings, + skip: querySkip, + uniqueQueryId, + }); + + return ( + + + {toggleStatus && ( + + + + + + )} + + ); +}; + +AlertsSummaryChartsPanel.displayName = 'AlertsSummaryChartsPanel'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx new file mode 100644 index 0000000000000..dcecb625a36db --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiBasicTable } from '@elastic/eui'; +import React, { memo, useCallback, useMemo, useState } from 'react'; +import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { ShapeTreeNode } from '@elastic/charts'; +import * as i18n from './translations'; +import type { ParsedAlertsData, SeverityBuckets } from './types'; +import type { FillColor } from '../../../../common/components/charts/donutchart'; +import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; +import { DonutChart } from '../../../../common/components/charts/donutchart'; +import { ChartLabel } from '../../../../overview/components/detection_response/alerts_by_status/chart_label'; +import { chartConfigs } from '../../../../overview/components/detection_response/alerts_by_status/alerts_by_status'; +import { HeaderSection } from '../../../../common/components/header_section'; +import { InspectButtonContainer } from '../../../../common/components/inspect'; +import { getSeverityTableColumns } from './columns'; + +const DONUT_HEIGHT = 150; + +interface AlertsChartsPanelProps { + data: ParsedAlertsData; + isLoading: boolean; + uniqueQueryId: string; +} + +export const SeverityLevelChart = memo( + ({ data, isLoading, uniqueQueryId }) => { + const [sortField, setSortField] = useState('value'); + const [sortDirection, setSortDirection] = useState('desc'); + + const fillColor: FillColor = useCallback((d: ShapeTreeNode) => { + return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor; + }, []); + + const onTableChange = useCallback( + ({ sort = {} }) => { + setSortField(sort.field); + setSortDirection(sort.direction); + }, + [setSortDirection, setSortField] + ); + + const columns = useMemo(() => getSeverityTableColumns(), []); + const items = data ?? []; + + const count = data + ? data.reduce(function (prev, cur) { + return prev + cur.value; + }, 0) + : 0; + + const sorting = useMemo(() => { + return { + sort: { + field: sortField, + direction: sortDirection, + }, + }; + }, [sortDirection, sortField]); + + return ( + + + + + + + + + + } + totalCount={count} + /> + + + + + + ); + } +); + +SeverityLevelChart.displayName = 'SeverityLevelChart'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts new file mode 100644 index 0000000000000..02f4cbc08f327 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; + +export const CHARTS_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.alerts.charts.chartsTitle', + { + defaultMessage: 'Charts', + } +); + +export const SEVERITY_LEVELS_INSPECT_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsBySeverity.inspectModalSeverityTitle', + { + defaultMessage: 'Severity levels', + } +); + +export const SEVERITY_LEVELS_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsBySeverity.inspectModalSeverityTitle', + { + defaultMessage: 'Severity Levels', + } +); + +export const SEVERITY_TOTAL_ALERTS = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsBySeverity.donut.totalAlerts', + { + defaultMessage: 'alerts', + } +); + +export const UNKNOWN_SEVERITY = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsBySeverity.unknown', + { + defaultMessage: 'Unknown', + } +); + +export const SEVERITY_LEVEL_COLUMN_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsBySeverity.tableColumnLevelTitle', + { + defaultMessage: 'Levels', + } +); + +export const SEVERITY_COUNT_COULMN_TITLE = i18n.translate( + 'xpack.securitySolution.detectionResponse.alertsBySeverity.tableColumnCountTitle', + { + defaultMessage: 'Counts', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts new file mode 100644 index 0000000000000..a153636dac94c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; + +export interface SeverityBuckets { + key: Severity; + value: number; + label: string; +} + +export type ParsedAlertsData = SeverityBuckets[] | undefined | null; +export interface EntityFilter { + field: string; + value: string; +} + +interface SeverityBucket { + key: Severity; + doc_count: number; +} + +export interface AlertsBySeverityAgg { + statusBySeverity: { + doc_count_error_upper_bound: number; + sum_other_doc_count: number; + buckets: SeverityBucket[]; + }; +} + +export interface AlertsBySeverityResponse { + took: number; + _shards: { + total: number; + successful: number; + skipped: number; + failed: number; + }; + aggregations?: Aggregations; + hits: { + total: { + value: number; + relation: string; + }; + hits: Hit[]; + }; +} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts new file mode 100644 index 0000000000000..aa34e4ee6656d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState, useMemo } from 'react'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; +import { buildEsQuery } from '@kbn/es-query'; +import type { Filter, Query } from '@kbn/es-query'; +import type { AlertsBySeverityAgg, EntityFilter, ParsedAlertsData } from './types'; +import type { ESBoolQuery } from '../../../../../common/typed_json'; +import { useGlobalTime } from '../../../../common/containers/use_global_time'; +import { useQueryAlerts } from '../../../containers/detection_engine/alerts/use_query'; +import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/constants'; +// import { useQueryInspector } from '../../../../common/components/page/manage_query'; +import { useInspectButton } from '../common/hooks'; +import { parseAlertsData } from './helpers'; + +const getAlertsBySeverityQuery = ({ + additionalFilters = [], + from, + to, + entityFilter, + runtimeMappings, +}: { + from: string; + to: string; + entityFilter?: EntityFilter; + additionalFilters?: ESBoolQuery[]; + runtimeMappings?: MappingRuntimeFields; +}) => ({ + size: 0, + query: { + bool: { + filter: [ + ...additionalFilters, + { range: { '@timestamp': { gte: from, lte: to } } }, + ...(entityFilter + ? [ + { + term: { + [entityFilter.field]: entityFilter.value, + }, + }, + ] + : []), + ], + }, + }, + aggs: { + statusBySeverity: { + terms: { + field: 'kibana.alert.severity', + }, + }, + }, + runtime_mappings: runtimeMappings, +}); + +interface UseSeverityChartProps { + uniqueQueryId: string; + signalIndexName: string | null; + skip?: boolean; + entityFilter?: EntityFilter; + query?: Query; + filters?: Filter[]; + runtimeMappings?: MappingRuntimeFields; +} + +export type UseAlertsByStatus = (props: UseSeverityChartProps) => { + items: ParsedAlertsData; + isLoading: boolean; + updatedAt: number; +}; + +export const useSeverityChartData: UseAlertsByStatus = ({ + uniqueQueryId, + entityFilter, + query, + filters, + runtimeMappings, + signalIndexName, + skip, +}) => { + const { to, from, deleteQuery, setQuery } = useGlobalTime(); + const [updatedAt, setUpdatedAt] = useState(Date.now()); + const [items, setItems] = useState(null); + + const additionalFilters = useMemo(() => { + try { + return [ + buildEsQuery( + undefined, + query != null ? [query] : [], + filters?.filter((f) => f.meta.disabled === false) ?? [] + ), + ]; + } catch (e) { + return []; + } + }, [query, filters]); + + const { + data, + loading: isLoading, + refetch: refetchQuery, + request, + response, + setQuery: setAlertsQuery, + } = useQueryAlerts<{}, AlertsBySeverityAgg>({ + query: getAlertsBySeverityQuery({ + from, + to, + entityFilter, + additionalFilters, + runtimeMappings, + }), + indexName: signalIndexName, + skip, + queryName: ALERTS_QUERY_NAMES.COUNT, + }); + + useEffect(() => { + setAlertsQuery( + getAlertsBySeverityQuery({ + from, + to, + entityFilter, + additionalFilters, + runtimeMappings, + }) + ); + }, [setAlertsQuery, from, to, entityFilter, additionalFilters, runtimeMappings]); + + useEffect(() => { + if (data == null) { + setItems(null); + } else { + setItems(parseAlertsData(data)); + } + setUpdatedAt(Date.now()); + }, [data]); + + const refetch = useCallback(() => { + if (!skip && refetchQuery) { + refetchQuery(); + } + }, [skip, refetchQuery]); + + // useQueryInspector({ + // deleteQuery, + // inspect: { + // dsl: [request], + // response: [response], + // }, + // refetch, + // setQuery, + // queryId, + // loading: isLoading, + // }); + + useInspectButton({ + deleteQuery, + loading: isLoading, + response, + setQuery, + refetch, + request, + uniqueQueryId, + }); + + return { items, isLoading, updatedAt }; +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx index b9dc4eb7c2ec2..11a509d4bea98 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx @@ -21,7 +21,7 @@ import { AlertsTreemapPanel } from '../../../../common/components/alerts_treemap import type { UpdateDateRange } from '../../../../common/components/charts/common'; import { useEuiComboBoxReset } from '../../../../common/components/use_combo_box_reset'; import { AlertsHistogramPanel } from '../../../components/alerts_kpis/alerts_histogram_panel'; -import { AlertsChartsPanel } from '../../../components/alerts_kpis/alerts_charts_panel'; +import { AlertsSummaryChartsPanel } from '../../../components/alerts_kpis/alerts_summary_charts_panel'; import { DEFAULT_STACK_BY_FIELD, DEFAULT_STACK_BY_FIELD1, @@ -235,10 +235,8 @@ const ChartPanelsComponent: React.FC = ({ {isLoadingIndexPattern ? ( ) : ( - = [ +export const chartConfigs: Array<{ key: Severity; label: string; color: string }> = [ { key: 'critical', label: STATUS_CRITICAL_LABEL, color: SEVERITY_COLOR.critical }, { key: 'high', label: STATUS_HIGH_LABEL, color: SEVERITY_COLOR.high }, { key: 'medium', label: STATUS_MEDIUM_LABEL, color: SEVERITY_COLOR.medium }, diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts index ee7545f3f9023..c22d2f0109776 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts @@ -157,8 +157,10 @@ export const useAlertsByStatus: UseAlertsByStatus = ({ additionalFilters, }) ); + }, [setAlertsQuery, from, to, entityFilter, additionalFilters]); + useEffect(() => { if (data == null) { setItems(null); From 1eca5577b85a7002b63ea9cece4afaa664c63bcd Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Fri, 2 Dec 2022 18:26:04 -0600 Subject: [PATCH 03/12] clean up --- .../rule_details/execution_log_table/execution_log_table.tsx | 2 +- .../detection_response/alerts_by_status/use_alerts_by_status.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx index 1a443e79a0d80..072d3b6f58174 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx @@ -475,7 +475,7 @@ const ExecutionLogTableComponent: React.FC = ({ columns={executionLogColumns} items={items} loading={isFetching} - // sorting={sorting} + sorting={sorting} pagination={pagination} onChange={onTableChangeCallback} /> diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts index c22d2f0109776..ee7545f3f9023 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status.ts @@ -157,10 +157,8 @@ export const useAlertsByStatus: UseAlertsByStatus = ({ additionalFilters, }) ); - }, [setAlertsQuery, from, to, entityFilter, additionalFilters]); - useEffect(() => { if (data == null) { setItems(null); From 88ddf74e42e6987a796204c880b21ffd57e4e08e Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 5 Dec 2022 20:20:11 +0000 Subject: [PATCH 04/12] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../plugins/security_solution/common/experimental_features.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 247e996a62da4..91415ada0fe1a 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -85,7 +85,7 @@ export const allowedExperimentalValues = Object.freeze({ * Enables top charts on Alerts Page */ alertsPageChartsEnabled: false, - + /** * Keep DEPRECATED experimental flags that are documented to prevent failed upgrades. * https://www.elastic.co/guide/en/security/current/user-risk-score.html From ca1fffb3188703670c7e13de41b056fc01e4c21b Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Mon, 5 Dec 2022 17:23:54 -0600 Subject: [PATCH 05/12] add interaction, fix CI --- .../common/experimental_features.ts | 2 +- .../common/components/charts/donutchart.tsx | 6 +- .../alerts_summary_charts_panel/helpers.tsx | 4 +- .../alerts_summary_charts_panel/index.tsx | 3 + .../severity_level_chart.tsx | 151 ++++++++++-------- .../translations.ts | 2 +- .../use_severity_chart_data.ts | 12 -- .../chart_panels/chart_select/helpers.test.ts | 3 + .../detection_engine/chart_panels/index.tsx | 1 + .../alerts_by_status/alerts_by_status.tsx | 2 +- 10 files changed, 97 insertions(+), 89 deletions(-) diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 247e996a62da4..91415ada0fe1a 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -85,7 +85,7 @@ export const allowedExperimentalValues = Object.freeze({ * Enables top charts on Alerts Page */ alertsPageChartsEnabled: false, - + /** * Keep DEPRECATED experimental flags that are documented to prevent failed upgrades. * https://www.elastic.co/guide/en/security/current/user-risk-score.html diff --git a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx index 4a0a8db70c58c..499ac862c6375 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.tsx @@ -8,7 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip, useEuiTheme } from '@elastic/eui'; import React, { useMemo } from 'react'; -import type { Datum, NodeColorAccessor, PartialTheme } from '@elastic/charts'; +import type { Datum, NodeColorAccessor, PartialTheme, ElementClickListener } from '@elastic/charts'; import { Chart, Partition, @@ -48,6 +48,7 @@ export interface DonutChartProps { legendItems?: LegendItem[] | null | undefined; title: React.ReactElement | string | number | null; totalCount: number | null | undefined; + onElementClick?: ElementClickListener; } /* Make this position absolute in order to overlap the text onto the donut */ @@ -72,6 +73,7 @@ export const DonutChart = ({ legendItems, title, totalCount, + onElementClick, }: DonutChartProps) => { const theme = useTheme(); const { euiTheme } = useEuiTheme(); @@ -114,7 +116,7 @@ export const DonutChart = ({ ) : ( - + @@ -19,7 +19,7 @@ export const parseAlertsData = ( return { key: severity.key, value: severity.doc_count, - label: chartConfigs.find((cfg) => cfg.key === severity.key)?.label ?? i18n.UNKNOWN_SEVERITY, + label: severityLabels[severity.key] ?? i18n.UNKNOWN_SEVERITY, }; }); return data; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx index 065291ab76c25..9ff855c8d41dd 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx @@ -34,6 +34,7 @@ const PlaceHolder = ({ title }: { title: string }) => { interface Props { alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; filters?: Filter[]; + addFilter?: ({ field, value }: { field: string; value: string | number }) => void; panelHeight?: number; query?: Query; signalIndexName: string | null; @@ -44,6 +45,7 @@ interface Props { export const AlertsSummaryChartsPanel: React.FC = ({ alignHeader, filters, + addFilter, panelHeight, query, runtimeMappings, @@ -100,6 +102,7 @@ export const AlertsSummaryChartsPanel: React.FC = ({ data={severityData} isLoading={isLoading} uniqueQueryId={uniqueQueryId} + addFilter={addFilter} /> diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx index dcecb625a36db..4c3ee2c1760dd 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiBasicTable } from '@elastic/eui'; -import React, { memo, useCallback, useMemo, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiInMemoryTable } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import { isEmpty } from 'lodash/fp'; import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { ShapeTreeNode } from '@elastic/charts'; +import type { ShapeTreeNode, ElementClickListener } from '@elastic/charts'; import * as i18n from './translations'; import type { ParsedAlertsData, SeverityBuckets } from './types'; import type { FillColor } from '../../../../common/components/charts/donutchart'; @@ -26,82 +27,92 @@ interface AlertsChartsPanelProps { data: ParsedAlertsData; isLoading: boolean; uniqueQueryId: string; + addFilter?: ({ field, value }: { field: string; value: string | number }) => void; } -export const SeverityLevelChart = memo( - ({ data, isLoading, uniqueQueryId }) => { - const [sortField, setSortField] = useState('value'); - const [sortDirection, setSortDirection] = useState('desc'); +export const SeverityLevelChart: React.FC = ({ + data, + isLoading, + uniqueQueryId, + addFilter, +}) => { + const fillColor: FillColor = useCallback((d: ShapeTreeNode) => { + return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor; + }, []); - const fillColor: FillColor = useCallback((d: ShapeTreeNode) => { - return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor; - }, []); + const columns = useMemo(() => getSeverityTableColumns(), []); + const items = data ?? []; - const onTableChange = useCallback( - ({ sort = {} }) => { - setSortField(sort.field); - setSortDirection(sort.direction); - }, - [setSortDirection, setSortField] - ); - - const columns = useMemo(() => getSeverityTableColumns(), []); - const items = data ?? []; - - const count = data + const count = useMemo(() => { + return data ? data.reduce(function (prev, cur) { return prev + cur.value; }, 0) : 0; + }, [data]); + + const sorting: { sort: { field: keyof SeverityBuckets; direction: SortOrder } } = { + sort: { + field: 'value', + direction: 'desc', + }, + }; + + const onElementClick: ElementClickListener = useCallback( + (event) => { + const flattened = event.flat(2); + const level = + flattened.length > 0 && + 'groupByRollup' in flattened[0] && + flattened[0].groupByRollup != null + ? `${flattened[0].groupByRollup}` + : ''; - const sorting = useMemo(() => { - return { - sort: { - field: sortField, - direction: sortDirection, - }, - }; - }, [sortDirection, sortField]); + if (addFilter != null && !isEmpty(level.trim())) { + addFilter({ field: 'kibana.alert.severity', value: level.toLowerCase() }); + } + }, + [addFilter] + ); - return ( - - - - - - - - - - } - totalCount={count} - /> - - - - - - ); - } -); + return ( + + + + + + + + + + } + totalCount={count} + onElementClick={onElementClick} + /> + + + + + + ); +}; SeverityLevelChart.displayName = 'SeverityLevelChart'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts index 02f4cbc08f327..11082825cc3d7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/translations.ts @@ -21,7 +21,7 @@ export const SEVERITY_LEVELS_INSPECT_TITLE = i18n.translate( ); export const SEVERITY_LEVELS_TITLE = i18n.translate( - 'xpack.securitySolution.detectionResponse.alertsBySeverity.inspectModalSeverityTitle', + 'xpack.securitySolution.detectionResponse.alertsBySeverity.chartSeverityTitle', { defaultMessage: 'Severity Levels', } diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts index aa34e4ee6656d..0e8b2fa8c5404 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts @@ -149,18 +149,6 @@ export const useSeverityChartData: UseAlertsByStatus = ({ } }, [skip, refetchQuery]); - // useQueryInspector({ - // deleteQuery, - // inspect: { - // dsl: [request], - // response: [response], - // }, - // refetch, - // setQuery, - // queryId, - // loading: isLoading, - // }); - useInspectButton({ deleteQuery, loading: isLoading, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts index 7d24cee9b6533..4f9ac6ad641c3 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts @@ -55,6 +55,7 @@ describe('helpers', () => { alertViewSelection, closePopover, setAlertViewSelection, + isAlertsPageChartsEnabled: false, // remove after charts is implemented }); expect(panels[0].id).toEqual(0); @@ -65,6 +66,7 @@ describe('helpers', () => { alertViewSelection, closePopover, setAlertViewSelection, + isAlertsPageChartsEnabled: false, // remove after charts is implemented }); const item = panels[0].items?.find((x) => x['data-test-subj'] === alertViewSelection); @@ -78,6 +80,7 @@ describe('helpers', () => { alertViewSelection, closePopover, setAlertViewSelection, + isAlertsPageChartsEnabled: false, // remove after charts is implemented }); const item = panels[0].items?.find((x) => x['data-test-subj'] === alertViewSelection); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx index 11a509d4bea98..83df1b0018b1f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx @@ -237,6 +237,7 @@ const ChartPanelsComponent: React.FC = ({ ) : ( = [ +const chartConfigs: Array<{ key: Severity; label: string; color: string }> = [ { key: 'critical', label: STATUS_CRITICAL_LABEL, color: SEVERITY_COLOR.critical }, { key: 'high', label: STATUS_HIGH_LABEL, color: SEVERITY_COLOR.high }, { key: 'medium', label: STATUS_MEDIUM_LABEL, color: SEVERITY_COLOR.medium }, From cdd0c96e1d7f05ff52b57c66d2bbd6374be75a21 Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Mon, 5 Dec 2022 18:21:26 -0600 Subject: [PATCH 06/12] fix CI --- .../alerts_kpis/alerts_summary_charts_panel/helpers.tsx | 9 +++++++++ .../alerts_summary_charts_panel/severity_level_chart.tsx | 5 ++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx index 65a02364cc299..4fac3240233fe 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx @@ -4,9 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import type { AlertsBySeverityResponse, AlertsBySeverityAgg, ParsedAlertsData } from './types'; import * as i18n from './translations'; import { severityLabels } from '../../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; +import { RISK_SEVERITY_COLOUR } from '../../../../common/components/severity/common'; +import type { RiskSeverity } from '../../../../../common/search_strategy'; +import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; export const parseAlertsData = ( response: AlertsBySeverityResponse<{}, AlertsBySeverityAgg> @@ -24,3 +28,8 @@ export const parseAlertsData = ( }); return data; }; + +export const getSeverityColor = (severity: string) => { + const label = severityLabels[severity as Severity] ?? i18n.UNKNOWN_SEVERITY; + return RISK_SEVERITY_COLOUR[label as RiskSeverity] ?? emptyDonutColor; +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx index 4c3ee2c1760dd..0522999bbb5b4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx @@ -13,13 +13,12 @@ import type { ShapeTreeNode, ElementClickListener } from '@elastic/charts'; import * as i18n from './translations'; import type { ParsedAlertsData, SeverityBuckets } from './types'; import type { FillColor } from '../../../../common/components/charts/donutchart'; -import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; import { DonutChart } from '../../../../common/components/charts/donutchart'; import { ChartLabel } from '../../../../overview/components/detection_response/alerts_by_status/chart_label'; -import { chartConfigs } from '../../../../overview/components/detection_response/alerts_by_status/alerts_by_status'; import { HeaderSection } from '../../../../common/components/header_section'; import { InspectButtonContainer } from '../../../../common/components/inspect'; import { getSeverityTableColumns } from './columns'; +import { getSeverityColor } from './helpers'; const DONUT_HEIGHT = 150; @@ -37,7 +36,7 @@ export const SeverityLevelChart: React.FC = ({ addFilter, }) => { const fillColor: FillColor = useCallback((d: ShapeTreeNode) => { - return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor; + return getSeverityColor(d.dataName); }, []); const columns = useMemo(() => getSeverityTableColumns(), []); From 6194538c14946b4766b0bd5ea2293ec12c5e903b Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Wed, 7 Dec 2022 12:52:10 -0600 Subject: [PATCH 07/12] add unit tests --- .../helpers.test.tsx | 21 +++ .../alerts_summary_charts_panel/helpers.tsx | 7 +- .../index.test.tsx | 128 +++++++++++++++++ .../alerts_summary_charts_panel/index.tsx | 4 +- .../alerts_summary_charts_panel/mock_data.ts | 106 ++++++++++++++ .../severity_level_chart.test.tsx | 58 ++++++++ .../severity_level_chart.tsx | 5 +- .../use_severity_chart_data.test.tsx | 132 ++++++++++++++++++ .../use_severity_chart_data.ts | 10 +- .../chart_panels/chart_select/helpers.test.ts | 19 ++- .../test/security_solution_cypress/config.ts | 1 + 11 files changed, 473 insertions(+), 18 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx new file mode 100644 index 0000000000000..469209ed3ea88 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { parseAlertsData } from './helpers'; +import { parsedAlerts, mockAlertsData, mockAlertsEmptyData } from './mock_data'; +import type { AlertsBySeverityResponse, AlertsBySeverityAgg} from './types'; + +describe('parseAlertsData', () => { + test('parse alerts with data', () => { + const res = parseAlertsData(mockAlertsData as AlertsBySeverityResponse<{}, AlertsBySeverityAgg>); + expect(res).toEqual(parsedAlerts); + }); + + test('parse alerts without data', () => { + const res = parseAlertsData(mockAlertsEmptyData); + expect(res).toEqual(null); + }); +}); \ No newline at end of file diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx index 4fac3240233fe..f15fd469e22da 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx @@ -8,9 +8,9 @@ import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import type { AlertsBySeverityResponse, AlertsBySeverityAgg, ParsedAlertsData } from './types'; import * as i18n from './translations'; import { severityLabels } from '../../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; -import { RISK_SEVERITY_COLOUR } from '../../../../common/components/severity/common'; -import type { RiskSeverity } from '../../../../../common/search_strategy'; import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; +import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; + export const parseAlertsData = ( response: AlertsBySeverityResponse<{}, AlertsBySeverityAgg> @@ -30,6 +30,5 @@ export const parseAlertsData = ( }; export const getSeverityColor = (severity: string) => { - const label = severityLabels[severity as Severity] ?? i18n.UNKNOWN_SEVERITY; - return RISK_SEVERITY_COLOUR[label as RiskSeverity] ?? emptyDonutColor; + return SEVERITY_COLOR[severity.toLocaleLowerCase() as Severity] ?? emptyDonutColor; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx new file mode 100644 index 0000000000000..b0a1c68dd9c6d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx @@ -0,0 +1,128 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { act, render, waitFor } from '@testing-library/react'; +import React from 'react'; +import { mount } from 'enzyme'; +import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { TestProviders } from '../../../../common/mock'; +import { AlertsSummaryChartsPanel } from '.'; + +jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../common/containers/query_toggle'); + +jest.mock('react-router-dom', () => { + const actual = jest.requireActual('react-router-dom'); + return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; +}); + + +describe('AlertsChartsPanel', () => { + const defaultProps = { + signalIndexName: 'signalIndexName', + }; + + const mockSetToggle = jest.fn(); + const mockUseQueryToggle = useQueryToggle as jest.Mock; + beforeEach(() => { + mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle }); + }); + + afterEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + test('renders correctly', async() => { + const { container } = render( + + + + ); + await waitFor(() => { + expect(container.querySelector('[data-test-subj="alerts-charts-panel"]')).toBeInTheDocument(); + }); + }); + + test('it renders the header with the specified `alignHeader` alignment', async() => { + const { container } = render( + + + + ); + await waitFor(() => { + expect( + container.querySelector('[data-test-subj="headerSectionInnerFlexGroup"]')?.classList[1] + ).toContain('flexEnd'); + }); + }); + + describe('Query', () => { + test('it render with a illegal KQL', async () => { + await act(async () => { + jest.mock('@kbn/es-query', () => ({ + buildEsQuery: jest.fn().mockImplementation(() => { + throw new Error('Something went wrong'); + }), + })); + const props = { ...defaultProps, query: { query: 'host.name: "', language: 'kql' } }; + const { container } = render( + + + + ); + + await waitFor(() => { + expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); + }); + }); + }); + }); + + describe('toggleQuery', () => { + test('toggles', async () => { + const wrapper = mount( + + + + ); + await act(async () => { + wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); + await waitFor(() => { + expect(mockSetToggle).toBeCalledWith(false); + }); + wrapper.unmount(); + }); + }); + + test('toggleStatus=true, render', async () => { + const { container } = render( + + + + ); + await waitFor(async () => { + expect( + container.querySelector('[data-test-subj="alerts-charts-container"]') + ).toBeInTheDocument(); + }); + }); + + test('toggleStatus=false, hide', async () => { + mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: mockSetToggle }); + const { container } = render( + + + + ); + await waitFor(async () => { + expect( + container.querySelector('[data-test-subj="alerts-charts-container"]') + ).not.toBeInTheDocument(); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx index 9ff855c8d41dd..d96dc62c29dd5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx @@ -81,7 +81,7 @@ export const AlertsSummaryChartsPanel: React.FC = ({ return ( @@ -96,7 +96,7 @@ export const AlertsSummaryChartsPanel: React.FC = ({ toggleQuery={toggleQuery} /> {toggleStatus && ( - + { + const actual = jest.requireActual('react-router-dom'); + return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; +}); + + +describe('Severity level chart', () => { + const defaultProps = { + data: [], + isLoading: false, + uniqueQueryId: 'test-query-id', + }; + + afterEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + test('renders correctly', () => { + const { container } = render( + + + + ); + expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); + }); + + test('render HeaderSection', () => { + const { container } = render( + + + + ); + expect(container.querySelector(`[data-test-subj="header-section"]`)).toBeInTheDocument(); + }); + + test('inspect button renders correctly', () => { + const { container } = render( + + + + ); + expect(container.querySelector('[data-test-subj="inspect-icon-button"]')).toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx index 0522999bbb5b4..f39361872cecb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx @@ -86,10 +86,10 @@ export const SeverityLevelChart: React.FC = ({ titleSize="xs" hideSubtitle /> - + = ({ mockDateNow()) as unknown as DateConstructor['now']; + +const defaultUseQueryAlertsReturn = { + loading: false, + data: null, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); +jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), + }; +}); + +const mockUseGlobalTime = jest + .fn() + .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }); +jest.mock('../../../../common/containers/use_global_time', () => { + return { + useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), + }; +}); + +// helper function to render the hook +const renderUseSeverityChartData = (props: Partial = {}) => + renderHook>( + () => + useSeverityChartData({ + uniqueQueryId: 'test', + signalIndexName: 'signal-alerts', + ...props, + }), + { + wrapper: TestProviders, + } + ); + +describe('useSeverityChartData', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockDateNow.mockReturnValue(dateNow); + mockUseQueryAlerts.mockReturnValue(defaultUseQueryAlertsReturn); + }); + + it('should return default values', () => { + const { result } = renderUseSeverityChartData(); + + expect(result.current).toEqual({ + items: null, + isLoading: false, + updatedAt: dateNow, + }); + + expect(mockUseQueryAlerts).toBeCalledWith({ + query: alertsBySeverityQuery, + indexName: 'signal-alerts', + skip: false, + queryName: ALERTS_QUERY_NAMES.COUNT, + }); + }); + + it('should return parsed items', () => { + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockAlertsData, + }); + + const { result } = renderUseSeverityChartData(); + expect(result.current).toEqual({ + items: parsedAlerts, + isLoading: false, + updatedAt: dateNow, + }); + }); + + it('should return new updatedAt', () => { + const newDateNow = new Date('2022-04-08T14:00:00.000Z').valueOf(); + mockDateNow.mockReturnValue(newDateNow); // setUpdatedAt call + mockDateNow.mockReturnValueOnce(dateNow); // initialization call + + mockUseQueryAlerts.mockReturnValue({ + ...defaultUseQueryAlertsReturn, + data: mockAlertsData, + }); + + const { result } = renderUseSeverityChartData(); + + expect(mockDateNow).toHaveBeenCalled(); + expect(result.current).toEqual({ + items: parsedAlerts, + isLoading: false, + updatedAt: newDateNow, + }); + }); + + it('should skip the query', () => { + const { result } = renderUseSeverityChartData({ skip: true }); + + expect(mockUseQueryAlerts).toBeCalledWith({ + query: alertsBySeverityQuery, + indexName: 'signal-alerts', + skip: true, + queryName: ALERTS_QUERY_NAMES.COUNT, + }); + + expect(result.current).toEqual({ + items: null, + isLoading: false, + updatedAt: dateNow, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts index 0e8b2fa8c5404..76814d16c4cf3 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts @@ -18,7 +18,7 @@ import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/ import { useInspectButton } from '../common/hooks'; import { parseAlertsData } from './helpers'; -const getAlertsBySeverityQuery = ({ +export const getAlertsBySeverityQuery = ({ additionalFilters = [], from, to, @@ -59,7 +59,7 @@ const getAlertsBySeverityQuery = ({ runtime_mappings: runtimeMappings, }); -interface UseSeverityChartProps { +export interface UseSeverityChartProps { uniqueQueryId: string; signalIndexName: string | null; skip?: boolean; @@ -69,20 +69,20 @@ interface UseSeverityChartProps { runtimeMappings?: MappingRuntimeFields; } -export type UseAlertsByStatus = (props: UseSeverityChartProps) => { +export type UseAlertsBySeverity = (props: UseSeverityChartProps) => { items: ParsedAlertsData; isLoading: boolean; updatedAt: number; }; -export const useSeverityChartData: UseAlertsByStatus = ({ +export const useSeverityChartData: UseAlertsBySeverity = ({ uniqueQueryId, entityFilter, query, filters, runtimeMappings, signalIndexName, - skip, + skip = false, }) => { const { to, from, deleteQuery, setQuery } = useGlobalTime(); const [updatedAt, setUpdatedAt] = useState(Date.now()); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts index 4f9ac6ad641c3..98e75c771921d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts @@ -5,13 +5,14 @@ * 2.0. */ -import type { AlertViewSelection } from './helpers'; import { + AlertViewSelection, getButtonProperties, getContextMenuPanels, TABLE_ID, TREEMAP_ID, TREND_ID, + CHARTS_ID, } from './helpers'; import * as i18n from './translations'; @@ -42,10 +43,18 @@ describe('helpers', () => { name: i18n.TREEMAP, }); }); + + test('it returns the expected properties when alertViewSelection is charts', () => { + expect(getButtonProperties(CHARTS_ID)).toEqual({ + 'data-test-subj': CHARTS_ID, + icon: 'visPie', + name: i18n.CHARTS, + }); + }); }); describe('getContextMenuPanels', () => { - const alertViewSelections: AlertViewSelection[] = ['trend', 'table', 'treemap']; + const alertViewSelections: AlertViewSelection[] = ['trend', 'table', 'treemap', 'charts']; const closePopover = jest.fn(); const setAlertViewSelection = jest.fn(); @@ -55,7 +64,7 @@ describe('helpers', () => { alertViewSelection, closePopover, setAlertViewSelection, - isAlertsPageChartsEnabled: false, // remove after charts is implemented + isAlertsPageChartsEnabled: true, // remove after charts is implemented }); expect(panels[0].id).toEqual(0); @@ -66,7 +75,7 @@ describe('helpers', () => { alertViewSelection, closePopover, setAlertViewSelection, - isAlertsPageChartsEnabled: false, // remove after charts is implemented + isAlertsPageChartsEnabled: true, // remove after charts is implemented }); const item = panels[0].items?.find((x) => x['data-test-subj'] === alertViewSelection); @@ -80,7 +89,7 @@ describe('helpers', () => { alertViewSelection, closePopover, setAlertViewSelection, - isAlertsPageChartsEnabled: false, // remove after charts is implemented + isAlertsPageChartsEnabled: true, // remove after charts is implemented }); const item = panels[0].items?.find((x) => x['data-test-subj'] === alertViewSelection); diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index 843d6457b55f1..f25bc13e04dc4 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -51,6 +51,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--xpack.ruleRegistry.unsafe.legacyMultiTenancy.enabled=true', `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'alertDetailsPageEnabled', + 'alertsPageChartsEnabled', ])}`, // mock cloud to enable the guided onboarding tour in e2e tests '--xpack.cloud.id=test', From ab014ac9d333e19bdd816df06de98057febd72e6 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 7 Dec 2022 19:07:45 +0000 Subject: [PATCH 08/12] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../helpers.test.tsx | 22 ++-- .../alerts_summary_charts_panel/helpers.tsx | 1 - .../index.test.tsx | 51 +++++---- .../alerts_summary_charts_panel/mock_data.ts | 106 +++++++++--------- .../severity_level_chart.test.tsx | 17 ++- .../use_severity_chart_data.test.tsx | 15 ++- .../chart_panels/chart_select/helpers.test.ts | 2 +- 7 files changed, 105 insertions(+), 109 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx index 469209ed3ea88..a2e63ceab4787 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx @@ -6,16 +6,18 @@ */ import { parseAlertsData } from './helpers'; import { parsedAlerts, mockAlertsData, mockAlertsEmptyData } from './mock_data'; -import type { AlertsBySeverityResponse, AlertsBySeverityAgg} from './types'; +import type { AlertsBySeverityResponse, AlertsBySeverityAgg } from './types'; describe('parseAlertsData', () => { - test('parse alerts with data', () => { - const res = parseAlertsData(mockAlertsData as AlertsBySeverityResponse<{}, AlertsBySeverityAgg>); - expect(res).toEqual(parsedAlerts); - }); + test('parse alerts with data', () => { + const res = parseAlertsData( + mockAlertsData as AlertsBySeverityResponse<{}, AlertsBySeverityAgg> + ); + expect(res).toEqual(parsedAlerts); + }); - test('parse alerts without data', () => { - const res = parseAlertsData(mockAlertsEmptyData); - expect(res).toEqual(null); - }); -}); \ No newline at end of file + test('parse alerts without data', () => { + const res = parseAlertsData(mockAlertsEmptyData); + expect(res).toEqual(null); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx index f15fd469e22da..0fc504a207199 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx @@ -11,7 +11,6 @@ import { severityLabels } from '../../../../overview/components/detection_respon import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; - export const parseAlertsData = ( response: AlertsBySeverityResponse<{}, AlertsBySeverityAgg> ): ParsedAlertsData => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx index b0a1c68dd9c6d..74f905fccf150 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx @@ -19,7 +19,6 @@ jest.mock('react-router-dom', () => { return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; }); - describe('AlertsChartsPanel', () => { const defaultProps = { signalIndexName: 'signalIndexName', @@ -36,28 +35,28 @@ describe('AlertsChartsPanel', () => { jest.restoreAllMocks(); }); - test('renders correctly', async() => { - const { container } = render( - - - - ); - await waitFor(() => { - expect(container.querySelector('[data-test-subj="alerts-charts-panel"]')).toBeInTheDocument(); - }); + test('renders correctly', async () => { + const { container } = render( + + + + ); + await waitFor(() => { + expect(container.querySelector('[data-test-subj="alerts-charts-panel"]')).toBeInTheDocument(); + }); }); - test('it renders the header with the specified `alignHeader` alignment', async() => { - const { container } = render( - - - - ); - await waitFor(() => { - expect( - container.querySelector('[data-test-subj="headerSectionInnerFlexGroup"]')?.classList[1] - ).toContain('flexEnd'); - }); + test('it renders the header with the specified `alignHeader` alignment', async () => { + const { container } = render( + + + + ); + await waitFor(() => { + expect( + container.querySelector('[data-test-subj="headerSectionInnerFlexGroup"]')?.classList[1] + ).toContain('flexEnd'); + }); }); describe('Query', () => { @@ -84,11 +83,11 @@ describe('AlertsChartsPanel', () => { describe('toggleQuery', () => { test('toggles', async () => { - const wrapper = mount( - - - - ); + const wrapper = mount( + + + + ); await act(async () => { wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); await waitFor(() => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts index 43dc0fe1a4a7c..c6c2404ec9f18 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts @@ -8,89 +8,88 @@ export const from = '2022-04-05T12:00:00.000Z'; export const to = '2022-04-08T12:00:00.000Z'; export const mockAlertsData = { - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0 + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 47, + relation: 'eq', }, - hits: { - total: { - value: 47, - relation: 'eq' + max_score: null, + hits: [], + }, + aggregations: { + statusBySeverity: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 78, + }, + { + key: 'low', + doc_count: 46, + }, + { + key: 'medium', + doc_count: 32, + }, + { + key: 'critical', + doc_count: 21, }, - max_score: null, - hits: [] + ], }, - aggregations: { - statusBySeverity: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 78 - }, - { - key: 'low', - doc_count: 46 - }, - { - key: 'medium', - doc_count: 32 - }, - { - key: 'critical', - doc_count: 21 - } - ] - } - } + }, }; export const parsedAlerts = [ - { key: 'high', value: 78, label: 'High' }, - { key: 'low', value: 46, label: 'Low' }, - { key: 'medium', value: 32, label: 'Medium' }, - { key: 'critical', value: 21, label: 'Critical' } - ]; - + { key: 'high', value: 78, label: 'High' }, + { key: 'low', value: 46, label: 'Low' }, + { key: 'medium', value: 32, label: 'Medium' }, + { key: 'critical', value: 21, label: 'Critical' }, +]; -export const mockAlertsEmptyData={ +export const mockAlertsEmptyData = { took: 0, timed_out: false, _shards: { total: 1, successful: 1, skipped: 0, - failed: 0 + failed: 0, }, hits: { total: { value: 0, - relation: 'eq' + relation: 'eq', }, max_score: null, - hits: [] + hits: [], }, aggregations: { statusBySeverity: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, - buckets: [] - } - } -} + buckets: [], + }, + }, +}; export const alertsBySeverityQuery = { size: 0, query: { bool: { filter: [ - { bool: { filter: [], must: [], must_not: [], should: []}}, - { range: { '@timestamp': { gte: from, lte: to } } } + { bool: { filter: [], must: [], must_not: [], should: [] } }, + { range: { '@timestamp': { gte: from, lte: to } } }, ], }, }, @@ -102,5 +101,4 @@ export const alertsBySeverityQuery = { }, }, runtime_mappings: undefined, - }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx index 9beb7fcbf15c5..c2ac9c18a0094 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { render} from '@testing-library/react'; +import { render } from '@testing-library/react'; import React from 'react'; import { TestProviders } from '../../../../common/mock'; import { SeverityLevelChart } from './severity_level_chart'; @@ -16,7 +16,6 @@ jest.mock('react-router-dom', () => { return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; }); - describe('Severity level chart', () => { const defaultProps = { data: [], @@ -31,13 +30,13 @@ describe('Severity level chart', () => { test('renders correctly', () => { const { container } = render( - - - - ); - expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); + + + + ); + expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); }); - + test('render HeaderSection', () => { const { container } = render( @@ -55,4 +54,4 @@ describe('Severity level chart', () => { ); expect(container.querySelector('[data-test-subj="inspect-icon-button"]')).toBeInTheDocument(); }); -}); \ No newline at end of file +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx index 05de98a1092d3..5fb7304e9597a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx @@ -7,12 +7,11 @@ import { renderHook } from '@testing-library/react-hooks'; import { TestProviders } from '../../../../common/mock'; -import { ALERTS_QUERY_NAMES } from '../../../../detections/containers/detection_engine/alerts/constants'; +import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/constants'; import { mockAlertsData, alertsBySeverityQuery, parsedAlerts, from, to } from './mock_data'; import type { UseAlertsBySeverity, UseSeverityChartProps } from './use_severity_chart_data'; import { useSeverityChartData } from './use_severity_chart_data'; - const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); const mockDateNow = jest.fn().mockReturnValue(dateNow); Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; @@ -26,7 +25,7 @@ const defaultUseQueryAlertsReturn = { refetch: () => {}, }; const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); -jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { +jest.mock('../../../containers/detection_engine/alerts/use_query', () => { return { useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), }; @@ -45,11 +44,11 @@ jest.mock('../../../../common/containers/use_global_time', () => { const renderUseSeverityChartData = (props: Partial = {}) => renderHook>( () => - useSeverityChartData({ - uniqueQueryId: 'test', - signalIndexName: 'signal-alerts', - ...props, - }), + useSeverityChartData({ + uniqueQueryId: 'test', + signalIndexName: 'signal-alerts', + ...props, + }), { wrapper: TestProviders, } diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts index 98e75c771921d..fe4cf26fd2c7d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts @@ -5,8 +5,8 @@ * 2.0. */ +import type { AlertViewSelection } from './helpers'; import { - AlertViewSelection, getButtonProperties, getContextMenuPanels, TABLE_ID, From 5d17e450bb160947eda8778fc1825ad8baf55884 Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Thu, 8 Dec 2022 17:26:48 -0600 Subject: [PATCH 09/12] clean up tests, move severity donut into its own folder --- .../helpers.test.tsx | 26 ++--- .../alerts_summary_charts_panel/helpers.tsx | 9 +- .../index.test.tsx | 69 ++++++------ .../alerts_summary_charts_panel/index.tsx | 8 +- .../alerts_summary_charts_panel/mock_data.ts | 106 ------------------ .../severity_donut/mock_data.ts | 104 +++++++++++++++++ .../severity_level_chart.test.tsx | 21 ++-- .../severity_level_chart.tsx | 18 +-- .../use_severity_chart_data.test.tsx | 19 ++-- .../use_severity_chart_data.ts | 20 ++-- .../alerts_summary_charts_panel/types.ts | 20 ++-- .../test/security_solution_cypress/config.ts | 1 - 12 files changed, 204 insertions(+), 217 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/mock_data.ts rename x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/{ => severity_donut}/severity_level_chart.test.tsx (79%) rename x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/{ => severity_donut}/severity_level_chart.tsx (83%) rename x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/{ => severity_donut}/use_severity_chart_data.test.tsx (88%) rename x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/{ => severity_donut}/use_severity_chart_data.ts (83%) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx index 469209ed3ea88..f78ac40a0766d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx @@ -4,18 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { parseAlertsData } from './helpers'; -import { parsedAlerts, mockAlertsData, mockAlertsEmptyData } from './mock_data'; -import type { AlertsBySeverityResponse, AlertsBySeverityAgg} from './types'; +import { parseSeverityAlerts } from './helpers'; +import { parsedAlerts, mockAlertsData, mockAlertsEmptyData } from './severity_donut/mock_data'; +import type { AlertsResponse, AlertsBySeverityAgg } from './types'; -describe('parseAlertsData', () => { - test('parse alerts with data', () => { - const res = parseAlertsData(mockAlertsData as AlertsBySeverityResponse<{}, AlertsBySeverityAgg>); - expect(res).toEqual(parsedAlerts); - }); +describe('parse alerts by severity data', () => { + test('parse alerts with data', () => { + const res = parseSeverityAlerts(mockAlertsData as AlertsResponse<{}, AlertsBySeverityAgg>); + expect(res).toEqual(parsedAlerts); + }); - test('parse alerts without data', () => { - const res = parseAlertsData(mockAlertsEmptyData); - expect(res).toEqual(null); - }); -}); \ No newline at end of file + test('parse alerts without data', () => { + const res = parseSeverityAlerts(mockAlertsEmptyData); + expect(res).toEqual(null); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx index f15fd469e22da..2737d6d33e961 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx @@ -5,16 +5,15 @@ * 2.0. */ import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; -import type { AlertsBySeverityResponse, AlertsBySeverityAgg, ParsedAlertsData } from './types'; +import type { AlertsResponse, AlertsBySeverityAgg, ParsedSeverityData } from './types'; import * as i18n from './translations'; import { severityLabels } from '../../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; - -export const parseAlertsData = ( - response: AlertsBySeverityResponse<{}, AlertsBySeverityAgg> -): ParsedAlertsData => { +export const parseSeverityAlerts = ( + response: AlertsResponse<{}, AlertsBySeverityAgg> +): ParsedSeverityData => { const severityBuckets = response?.aggregations?.statusBySeverity?.buckets ?? []; if (severityBuckets.length === 0) { return null; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx index b0a1c68dd9c6d..a885eca625c4d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.test.tsx @@ -4,9 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { act, render, waitFor } from '@testing-library/react'; +import { act, render, fireEvent } from '@testing-library/react'; import React from 'react'; -import { mount } from 'enzyme'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; import { TestProviders } from '../../../../common/mock'; import { AlertsSummaryChartsPanel } from '.'; @@ -19,7 +18,6 @@ jest.mock('react-router-dom', () => { return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; }); - describe('AlertsChartsPanel', () => { const defaultProps = { signalIndexName: 'signalIndexName', @@ -36,28 +34,28 @@ describe('AlertsChartsPanel', () => { jest.restoreAllMocks(); }); - test('renders correctly', async() => { + test('renders correctly', async () => { + await act(async () => { const { container } = render( ); - await waitFor(() => { - expect(container.querySelector('[data-test-subj="alerts-charts-panel"]')).toBeInTheDocument(); - }); + expect(container.querySelector('[data-test-subj="alerts-charts-panel"]')).toBeInTheDocument(); + }); }); - test('it renders the header with the specified `alignHeader` alignment', async() => { + test('it renders the header with the specified `alignHeader` alignment', async () => { + await act(async () => { const { container } = render( ); - await waitFor(() => { - expect( - container.querySelector('[data-test-subj="headerSectionInnerFlexGroup"]')?.classList[1] - ).toContain('flexEnd'); - }); + expect( + container.querySelector('[data-test-subj="headerSectionInnerFlexGroup"]')?.classList[1] + ).toContain('flexEnd'); + }); }); describe('Query', () => { @@ -74,37 +72,34 @@ describe('AlertsChartsPanel', () => { ); - - await waitFor(() => { - expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); - }); + expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); }); }); }); describe('toggleQuery', () => { test('toggles', async () => { - const wrapper = mount( + await act(async () => { + const { container } = render( ); - await act(async () => { - wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); - await waitFor(() => { - expect(mockSetToggle).toBeCalledWith(false); - }); - wrapper.unmount(); + const element = container.querySelector('[data-test-subj="query-toggle-header"]'); + if (element) { + fireEvent.click(element); + } + expect(mockSetToggle).toBeCalledWith(false); }); }); test('toggleStatus=true, render', async () => { - const { container } = render( - - - - ); - await waitFor(async () => { + await act(async () => { + const { container } = render( + + + + ); expect( container.querySelector('[data-test-subj="alerts-charts-container"]') ).toBeInTheDocument(); @@ -112,13 +107,13 @@ describe('AlertsChartsPanel', () => { }); test('toggleStatus=false, hide', async () => { - mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: mockSetToggle }); - const { container } = render( - - - - ); - await waitFor(async () => { + await act(async () => { + mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: mockSetToggle }); + const { container } = render( + + + + ); expect( container.querySelector('[data-test-subj="alerts-charts-container"]') ).not.toBeInTheDocument(); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx index d96dc62c29dd5..a60b7d7ed4fa0 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/index.tsx @@ -14,8 +14,8 @@ import * as i18n from './translations'; import { KpiPanel } from '../common/components'; import { HeaderSection } from '../../../../common/components/header_section'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; -import { useSeverityChartData } from './use_severity_chart_data'; -import { SeverityLevelChart } from './severity_level_chart'; +import { useSeverityChartData } from './severity_donut/use_severity_chart_data'; +import { SeverityLevelChart } from './severity_donut/severity_level_chart'; const DETECTIONS_ALERTS_CHARTS_ID = 'detections-alerts-charts'; @@ -69,7 +69,7 @@ export const AlertsSummaryChartsPanel: React.FC = ({ [setQuerySkip, setToggleStatus] ); - const { items: severityData, isLoading } = useSeverityChartData({ + const { items: severityData, isLoading: isSeverityLoading } = useSeverityChartData({ filters, query, signalIndexName, @@ -100,7 +100,7 @@ export const AlertsSummaryChartsPanel: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts deleted file mode 100644 index 43dc0fe1a4a7c..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -export const from = '2022-04-05T12:00:00.000Z'; -export const to = '2022-04-08T12:00:00.000Z'; - -export const mockAlertsData = { - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0 - }, - hits: { - total: { - value: 47, - relation: 'eq' - }, - max_score: null, - hits: [] - }, - aggregations: { - statusBySeverity: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 78 - }, - { - key: 'low', - doc_count: 46 - }, - { - key: 'medium', - doc_count: 32 - }, - { - key: 'critical', - doc_count: 21 - } - ] - } - } -}; - -export const parsedAlerts = [ - { key: 'high', value: 78, label: 'High' }, - { key: 'low', value: 46, label: 'Low' }, - { key: 'medium', value: 32, label: 'Medium' }, - { key: 'critical', value: 21, label: 'Critical' } - ]; - - -export const mockAlertsEmptyData={ - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0 - }, - hits: { - total: { - value: 0, - relation: 'eq' - }, - max_score: null, - hits: [] - }, - aggregations: { - statusBySeverity: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [] - } - } -} - -export const alertsBySeverityQuery = { - size: 0, - query: { - bool: { - filter: [ - { bool: { filter: [], must: [], must_not: [], should: []}}, - { range: { '@timestamp': { gte: from, lte: to } } } - ], - }, - }, - aggs: { - statusBySeverity: { - terms: { - field: 'kibana.alert.severity', - }, - }, - }, - runtime_mappings: undefined, - -}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/mock_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/mock_data.ts new file mode 100644 index 0000000000000..c6c2404ec9f18 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/mock_data.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export const from = '2022-04-05T12:00:00.000Z'; +export const to = '2022-04-08T12:00:00.000Z'; + +export const mockAlertsData = { + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 47, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + statusBySeverity: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 78, + }, + { + key: 'low', + doc_count: 46, + }, + { + key: 'medium', + doc_count: 32, + }, + { + key: 'critical', + doc_count: 21, + }, + ], + }, + }, +}; + +export const parsedAlerts = [ + { key: 'high', value: 78, label: 'High' }, + { key: 'low', value: 46, label: 'Low' }, + { key: 'medium', value: 32, label: 'Medium' }, + { key: 'critical', value: 21, label: 'Critical' }, +]; + +export const mockAlertsEmptyData = { + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 0, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + statusBySeverity: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, +}; + +export const alertsBySeverityQuery = { + size: 0, + query: { + bool: { + filter: [ + { bool: { filter: [], must: [], must_not: [], should: [] } }, + { range: { '@timestamp': { gte: from, lte: to } } }, + ], + }, + }, + aggs: { + statusBySeverity: { + terms: { + field: 'kibana.alert.severity', + }, + }, + }, + runtime_mappings: undefined, +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.test.tsx similarity index 79% rename from x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx rename to x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.test.tsx index 9beb7fcbf15c5..3b6b262efb545 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.test.tsx @@ -4,19 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { render} from '@testing-library/react'; +import { render } from '@testing-library/react'; import React from 'react'; -import { TestProviders } from '../../../../common/mock'; +import { TestProviders } from '../../../../../common/mock'; import { SeverityLevelChart } from './severity_level_chart'; -jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../../../common/lib/kibana'); jest.mock('react-router-dom', () => { const actual = jest.requireActual('react-router-dom'); return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; }); - describe('Severity level chart', () => { const defaultProps = { data: [], @@ -31,13 +30,13 @@ describe('Severity level chart', () => { test('renders correctly', () => { const { container } = render( - - - - ); - expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); + + + + ); + expect(container.querySelector('[data-test-subj="severty-chart"]')).toBeInTheDocument(); }); - + test('render HeaderSection', () => { const { container } = render( @@ -55,4 +54,4 @@ describe('Severity level chart', () => { ); expect(container.querySelector('[data-test-subj="inspect-icon-button"]')).toBeInTheDocument(); }); -}); \ No newline at end of file +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx similarity index 83% rename from x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx rename to x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx index f39361872cecb..00433c87ee127 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_level_chart.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx @@ -10,15 +10,15 @@ import React, { useCallback, useMemo } from 'react'; import { isEmpty } from 'lodash/fp'; import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ShapeTreeNode, ElementClickListener } from '@elastic/charts'; -import * as i18n from './translations'; -import type { ParsedAlertsData, SeverityBuckets } from './types'; -import type { FillColor } from '../../../../common/components/charts/donutchart'; -import { DonutChart } from '../../../../common/components/charts/donutchart'; -import { ChartLabel } from '../../../../overview/components/detection_response/alerts_by_status/chart_label'; -import { HeaderSection } from '../../../../common/components/header_section'; -import { InspectButtonContainer } from '../../../../common/components/inspect'; -import { getSeverityTableColumns } from './columns'; -import { getSeverityColor } from './helpers'; +import * as i18n from '../translations'; +import type { ParsedAlertsData, SeverityBuckets } from '../types'; +import type { FillColor } from '../../../../../common/components/charts/donutchart'; +import { DonutChart } from '../../../../../common/components/charts/donutchart'; +import { ChartLabel } from '../../../../../overview/components/detection_response/alerts_by_status/chart_label'; +import { HeaderSection } from '../../../../../common/components/header_section'; +import { InspectButtonContainer } from '../../../../../common/components/inspect'; +import { getSeverityTableColumns } from '../columns'; +import { getSeverityColor } from '../helpers'; const DONUT_HEIGHT = 150; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx similarity index 88% rename from x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx rename to x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx index 05de98a1092d3..0d48d023d06a3 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx @@ -6,13 +6,12 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { TestProviders } from '../../../../common/mock'; -import { ALERTS_QUERY_NAMES } from '../../../../detections/containers/detection_engine/alerts/constants'; +import { TestProviders } from '../../../../../common/mock'; +import { ALERTS_QUERY_NAMES } from '../../../../containers/detection_engine/alerts/constants'; import { mockAlertsData, alertsBySeverityQuery, parsedAlerts, from, to } from './mock_data'; import type { UseAlertsBySeverity, UseSeverityChartProps } from './use_severity_chart_data'; import { useSeverityChartData } from './use_severity_chart_data'; - const dateNow = new Date('2022-04-08T12:00:00.000Z').valueOf(); const mockDateNow = jest.fn().mockReturnValue(dateNow); Date.now = jest.fn(() => mockDateNow()) as unknown as DateConstructor['now']; @@ -26,7 +25,7 @@ const defaultUseQueryAlertsReturn = { refetch: () => {}, }; const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); -jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', () => { +jest.mock('../../../../containers/detection_engine/alerts/use_query', () => { return { useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), }; @@ -35,7 +34,7 @@ jest.mock('../../../../detections/containers/detection_engine/alerts/use_query', const mockUseGlobalTime = jest .fn() .mockReturnValue({ from, to, setQuery: jest.fn(), deleteQuery: jest.fn() }); -jest.mock('../../../../common/containers/use_global_time', () => { +jest.mock('../../../../../common/containers/use_global_time', () => { return { useGlobalTime: (...props: unknown[]) => mockUseGlobalTime(...props), }; @@ -45,11 +44,11 @@ jest.mock('../../../../common/containers/use_global_time', () => { const renderUseSeverityChartData = (props: Partial = {}) => renderHook>( () => - useSeverityChartData({ - uniqueQueryId: 'test', - signalIndexName: 'signal-alerts', - ...props, - }), + useSeverityChartData({ + uniqueQueryId: 'test', + signalIndexName: 'signal-alerts', + ...props, + }), { wrapper: TestProviders, } diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.ts similarity index 83% rename from x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts rename to x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.ts index 76814d16c4cf3..cfc1353335b9f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.ts @@ -9,14 +9,14 @@ import { useCallback, useEffect, useState, useMemo } from 'react'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import { buildEsQuery } from '@kbn/es-query'; import type { Filter, Query } from '@kbn/es-query'; -import type { AlertsBySeverityAgg, EntityFilter, ParsedAlertsData } from './types'; -import type { ESBoolQuery } from '../../../../../common/typed_json'; -import { useGlobalTime } from '../../../../common/containers/use_global_time'; -import { useQueryAlerts } from '../../../containers/detection_engine/alerts/use_query'; -import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/constants'; +import type { AlertsBySeverityAgg, EntityFilter, ParsedSeverityData } from '../types'; +import type { ESBoolQuery } from '../../../../../../common/typed_json'; +import { useGlobalTime } from '../../../../../common/containers/use_global_time'; +import { useQueryAlerts } from '../../../../containers/detection_engine/alerts/use_query'; +import { ALERTS_QUERY_NAMES } from '../../../../containers/detection_engine/alerts/constants'; // import { useQueryInspector } from '../../../../common/components/page/manage_query'; -import { useInspectButton } from '../common/hooks'; -import { parseAlertsData } from './helpers'; +import { useInspectButton } from '../../common/hooks'; +import { parseSeverityAlerts } from '../helpers'; export const getAlertsBySeverityQuery = ({ additionalFilters = [], @@ -70,7 +70,7 @@ export interface UseSeverityChartProps { } export type UseAlertsBySeverity = (props: UseSeverityChartProps) => { - items: ParsedAlertsData; + items: ParsedSeverityData; isLoading: boolean; updatedAt: number; }; @@ -86,7 +86,7 @@ export const useSeverityChartData: UseAlertsBySeverity = ({ }) => { const { to, from, deleteQuery, setQuery } = useGlobalTime(); const [updatedAt, setUpdatedAt] = useState(Date.now()); - const [items, setItems] = useState(null); + const [items, setItems] = useState(null); const additionalFilters = useMemo(() => { try { @@ -138,7 +138,7 @@ export const useSeverityChartData: UseAlertsBySeverity = ({ if (data == null) { setItems(null); } else { - setItems(parseAlertsData(data)); + setItems(parseSeverityAlerts(data)); } setUpdatedAt(Date.now()); }, [data]); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts index a153636dac94c..5230a4f06f6fa 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/types.ts @@ -6,21 +6,16 @@ */ import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; -export interface SeverityBuckets { - key: Severity; - value: number; - label: string; -} - -export type ParsedAlertsData = SeverityBuckets[] | undefined | null; export interface EntityFilter { field: string; value: string; } -interface SeverityBucket { +export type ParsedSeverityData = SeverityData[] | undefined | null; +export interface SeverityData { key: Severity; - doc_count: number; + value: number; + label: string; } export interface AlertsBySeverityAgg { @@ -30,8 +25,11 @@ export interface AlertsBySeverityAgg { buckets: SeverityBucket[]; }; } - -export interface AlertsBySeverityResponse { +interface SeverityBucket { + key: Severity; + doc_count: number; +} +export interface AlertsResponse { took: number; _shards: { total: number; diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts index 38bd72fd2b607..81d18ce1cab5d 100644 --- a/x-pack/test/security_solution_cypress/config.ts +++ b/x-pack/test/security_solution_cypress/config.ts @@ -48,7 +48,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--xpack.ruleRegistry.unsafe.legacyMultiTenancy.enabled=true', `--xpack.securitySolution.enableExperimental=${JSON.stringify([ 'alertDetailsPageEnabled', - 'alertsPageChartsEnabled', ])}`, // mock cloud to enable the guided onboarding tour in e2e tests '--xpack.cloud.id=test', From 945e279106ba77adce01315a90a26ffb29fc0598 Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Thu, 8 Dec 2022 17:39:09 -0600 Subject: [PATCH 10/12] fix reference --- .../helpers.test.tsx | 16 --- .../alerts_summary_charts_panel/helpers.tsx | 6 - .../alerts_summary_charts_panel/mock_data.ts | 104 ------------------ .../severity_donut/severity_level_chart.tsx | 6 +- .../use_severity_chart_data.test.tsx | 9 -- 5 files changed, 3 insertions(+), 138 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx index ec4a56ab16507..f78ac40a0766d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.test.tsx @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -<<<<<<< HEAD import { parseSeverityAlerts } from './helpers'; import { parsedAlerts, mockAlertsData, mockAlertsEmptyData } from './severity_donut/mock_data'; import type { AlertsResponse, AlertsBySeverityAgg } from './types'; @@ -12,26 +11,11 @@ import type { AlertsResponse, AlertsBySeverityAgg } from './types'; describe('parse alerts by severity data', () => { test('parse alerts with data', () => { const res = parseSeverityAlerts(mockAlertsData as AlertsResponse<{}, AlertsBySeverityAgg>); -======= -import { parseAlertsData } from './helpers'; -import { parsedAlerts, mockAlertsData, mockAlertsEmptyData } from './mock_data'; -import type { AlertsBySeverityResponse, AlertsBySeverityAgg } from './types'; - -describe('parseAlertsData', () => { - test('parse alerts with data', () => { - const res = parseAlertsData( - mockAlertsData as AlertsBySeverityResponse<{}, AlertsBySeverityAgg> - ); ->>>>>>> 4f0745e5da5b22122d18b2b9dce99737ad2e8f18 expect(res).toEqual(parsedAlerts); }); test('parse alerts without data', () => { -<<<<<<< HEAD const res = parseSeverityAlerts(mockAlertsEmptyData); -======= - const res = parseAlertsData(mockAlertsEmptyData); ->>>>>>> 4f0745e5da5b22122d18b2b9dce99737ad2e8f18 expect(res).toEqual(null); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx index e56170538fa2a..2737d6d33e961 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/helpers.tsx @@ -11,15 +11,9 @@ import { severityLabels } from '../../../../overview/components/detection_respon import { emptyDonutColor } from '../../../../common/components/charts/donutchart_empty'; import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; -<<<<<<< HEAD export const parseSeverityAlerts = ( response: AlertsResponse<{}, AlertsBySeverityAgg> ): ParsedSeverityData => { -======= -export const parseAlertsData = ( - response: AlertsBySeverityResponse<{}, AlertsBySeverityAgg> -): ParsedAlertsData => { ->>>>>>> 4f0745e5da5b22122d18b2b9dce99737ad2e8f18 const severityBuckets = response?.aggregations?.statusBySeverity?.buckets ?? []; if (severityBuckets.length === 0) { return null; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts deleted file mode 100644 index c6c2404ec9f18..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/mock_data.ts +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -export const from = '2022-04-05T12:00:00.000Z'; -export const to = '2022-04-08T12:00:00.000Z'; - -export const mockAlertsData = { - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 47, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - aggregations: { - statusBySeverity: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 78, - }, - { - key: 'low', - doc_count: 46, - }, - { - key: 'medium', - doc_count: 32, - }, - { - key: 'critical', - doc_count: 21, - }, - ], - }, - }, -}; - -export const parsedAlerts = [ - { key: 'high', value: 78, label: 'High' }, - { key: 'low', value: 46, label: 'Low' }, - { key: 'medium', value: 32, label: 'Medium' }, - { key: 'critical', value: 21, label: 'Critical' }, -]; - -export const mockAlertsEmptyData = { - took: 0, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - hits: { - total: { - value: 0, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - aggregations: { - statusBySeverity: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, - }, -}; - -export const alertsBySeverityQuery = { - size: 0, - query: { - bool: { - filter: [ - { bool: { filter: [], must: [], must_not: [], should: [] } }, - { range: { '@timestamp': { gte: from, lte: to } } }, - ], - }, - }, - aggs: { - statusBySeverity: { - terms: { - field: 'kibana.alert.severity', - }, - }, - }, - runtime_mappings: undefined, -}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx index 00433c87ee127..495b2f654f420 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx @@ -11,7 +11,7 @@ import { isEmpty } from 'lodash/fp'; import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ShapeTreeNode, ElementClickListener } from '@elastic/charts'; import * as i18n from '../translations'; -import type { ParsedAlertsData, SeverityBuckets } from '../types'; +import type { ParsedSeverityData, SeverityData } from '../types'; import type { FillColor } from '../../../../../common/components/charts/donutchart'; import { DonutChart } from '../../../../../common/components/charts/donutchart'; import { ChartLabel } from '../../../../../overview/components/detection_response/alerts_by_status/chart_label'; @@ -23,7 +23,7 @@ import { getSeverityColor } from '../helpers'; const DONUT_HEIGHT = 150; interface AlertsChartsPanelProps { - data: ParsedAlertsData; + data: ParsedSeverityData; isLoading: boolean; uniqueQueryId: string; addFilter?: ({ field, value }: { field: string; value: string | number }) => void; @@ -50,7 +50,7 @@ export const SeverityLevelChart: React.FC = ({ : 0; }, [data]); - const sorting: { sort: { field: keyof SeverityBuckets; direction: SortOrder } } = { + const sorting: { sort: { field: keyof SeverityData; direction: SortOrder } } = { sort: { field: 'value', direction: 'desc', diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx index c979d24deb036..0d48d023d06a3 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx @@ -6,13 +6,8 @@ */ import { renderHook } from '@testing-library/react-hooks'; -<<<<<<< HEAD:x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx import { TestProviders } from '../../../../../common/mock'; import { ALERTS_QUERY_NAMES } from '../../../../containers/detection_engine/alerts/constants'; -======= -import { TestProviders } from '../../../../common/mock'; -import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/constants'; ->>>>>>> 4f0745e5da5b22122d18b2b9dce99737ad2e8f18:x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx import { mockAlertsData, alertsBySeverityQuery, parsedAlerts, from, to } from './mock_data'; import type { UseAlertsBySeverity, UseSeverityChartProps } from './use_severity_chart_data'; import { useSeverityChartData } from './use_severity_chart_data'; @@ -30,11 +25,7 @@ const defaultUseQueryAlertsReturn = { refetch: () => {}, }; const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); -<<<<<<< HEAD:x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.test.tsx jest.mock('../../../../containers/detection_engine/alerts/use_query', () => { -======= -jest.mock('../../../containers/detection_engine/alerts/use_query', () => { ->>>>>>> 4f0745e5da5b22122d18b2b9dce99737ad2e8f18:x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/use_severity_chart_data.test.tsx return { useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), }; From fadcb9b65afe25dd83e4d226cfdd9ac8da7b90be Mon Sep 17 00:00:00 2001 From: Christine Weng Date: Mon, 12 Dec 2022 11:17:11 -0600 Subject: [PATCH 11/12] address feedback --- .../alerts_summary_charts_panel/columns.tsx | 5 ++-- .../alerts_summary_charts_panel/index.tsx | 4 +-- .../severity_donut/severity_level_chart.tsx | 5 ++-- .../severity_donut/use_severity_chart_data.ts | 1 - .../translations.ts | 25 ++++++++++++------- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx index 51f3bdab3ae2a..ee9de4667ec26 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/columns.tsx @@ -9,6 +9,7 @@ import { EuiHealth, EuiText } from '@elastic/eui'; import type { EuiBasicTableColumn } from '@elastic/eui'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; import { capitalize } from 'lodash'; +import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; import { DefaultDraggable } from '../../../../common/components/draggables'; import { SEVERITY_COLOR } from '../../../../overview/components/detection_response/utils'; import { FormattedCount } from '../../../../common/components/formatted_number'; @@ -29,8 +30,8 @@ export const getSeverityTableColumns = (): Array = ({ /> {toggleStatus && ( - + - + )} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx index 495b2f654f420..1183c66735f29 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/severity_level_chart.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiInMemoryTable } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { isEmpty } from 'lodash/fp'; +import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ShapeTreeNode, ElementClickListener } from '@elastic/charts'; import * as i18n from '../translations'; @@ -68,7 +69,7 @@ export const SeverityLevelChart: React.FC = ({ : ''; if (addFilter != null && !isEmpty(level.trim())) { - addFilter({ field: 'kibana.alert.severity', value: level.toLowerCase() }); + addFilter({ field: ALERT_SEVERITY, value: level.toLowerCase() }); } }, [addFilter] @@ -80,7 +81,7 @@ export const SeverityLevelChart: React.FC = ({ Date: Mon, 12 Dec 2022 13:47:58 -0600 Subject: [PATCH 12/12] fix reference in use_severity_data --- .../severity_donut/use_severity_chart_data.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.ts index 41657dd227c3e..ce51c78f3f64e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_summary_charts_panel/severity_donut/use_severity_chart_data.ts @@ -8,6 +8,7 @@ import { useCallback, useEffect, useState, useMemo } from 'react'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import { buildEsQuery } from '@kbn/es-query'; +import { ALERT_SEVERITY } from '@kbn/rule-data-utils'; import type { Filter, Query } from '@kbn/es-query'; import type { AlertsBySeverityAgg, EntityFilter, ParsedSeverityData } from '../types'; import type { ESBoolQuery } from '../../../../../../common/typed_json'; @@ -51,7 +52,7 @@ export const getAlertsBySeverityQuery = ({ aggs: { statusBySeverity: { terms: { - field: 'kibana.alert.severity', + field: ALERT_SEVERITY, }, }, },