From d9746f55c4baad4baa89d0be2a910c5434fd62c2 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Mon, 24 Apr 2023 13:19:32 -0600 Subject: [PATCH 01/24] combine updates --- .../alerts_table/alerts_grouping.tsx | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index 99ae3f4ff3433..4e04049553ac6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -132,10 +132,6 @@ const GroupedAlertsTableComponent: React.FC = (props) setPageIndex((curr) => curr.map(() => DEFAULT_PAGE_INDEX)); }, []); - useEffect(() => { - resetAllPagination(); - }, [resetAllPagination, selectedGroups]); - const setPageVar = useCallback( (newNumber: number, groupingLevel: number, pageType: 'index' | 'size') => { if (pageType === 'index') { @@ -158,23 +154,31 @@ const GroupedAlertsTableComponent: React.FC = (props) [setStoragePageSize] ); - const nonGroupingFilters = useRef({ + const paginationResetTriggers = useRef({ defaultFilters: props.defaultFilters, globalFilters: props.globalFilters, globalQuery: props.globalQuery, + selectedGroups, }); useEffect(() => { - const nonGrouping = { + const triggers = { defaultFilters: props.defaultFilters, globalFilters: props.globalFilters, globalQuery: props.globalQuery, + selectedGroups, }; - if (!isEqual(nonGroupingFilters.current, nonGrouping)) { + if (!isEqual(paginationResetTriggers.current, triggers)) { resetAllPagination(); - nonGroupingFilters.current = nonGrouping; + paginationResetTriggers.current = triggers; } - }, [props.defaultFilters, props.globalFilters, props.globalQuery, resetAllPagination]); + }, [ + props.defaultFilters, + props.globalFilters, + props.globalQuery, + resetAllPagination, + selectedGroups, + ]); const getLevel = useCallback( (level: number, selectedGroup: string, parentGroupingFilter?: string) => { From 571e06dda218d8404cc5737a475ca72dce2c0196 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Mon, 24 Apr 2023 15:29:38 -0600 Subject: [PATCH 02/24] fixed it --- .../src/components/accordion_panel/helpers.ts | 19 ++++ .../src/components/accordion_panel/index.tsx | 11 ++- .../src/components/grouping.tsx | 89 +++++++++++++++++-- .../src/components/types.ts | 2 + .../src/containers/query/index.ts | 17 ++++ .../grouping_settings/query_builder.ts | 1 + 6 files changed, 128 insertions(+), 11 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts index 5529d5bf521c0..14d7c44daf1f1 100644 --- a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/helpers.ts @@ -30,3 +30,22 @@ export const createGroupFilter = (selectedGroup: string, query?: string) => }, ] : []; + +export const getNullGroupFilter = (selectedGroup: string) => [ + { + meta: { + disabled: false, + negate: true, + alias: null, + key: selectedGroup, + field: selectedGroup, + value: 'exists', + type: 'exists', + }, + query: { + exists: { + field: selectedGroup, + }, + }, + }, +]; diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx index c1d55495cf785..368957a1335a6 100644 --- a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx @@ -11,7 +11,7 @@ import type { Filter } from '@kbn/es-query'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { firstNonNullValue } from '../../helpers'; import type { RawBucket } from '../types'; -import { createGroupFilter } from './helpers'; +import { createGroupFilter, getNullGroupFilter } from './helpers'; interface GroupPanelProps { customAccordionButtonClassName?: string; @@ -22,6 +22,7 @@ interface GroupPanelProps { groupPanelRenderer?: JSX.Element; groupingLevel?: number; isLoading: boolean; + isNullGroup?: boolean; onGroupClose: () => void; onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket) => void; renderChildComponent: (groupFilter: Filter[]) => React.ReactElement; @@ -49,6 +50,7 @@ const GroupPanelComponent = ({ groupPanelRenderer, groupingLevel = 0, isLoading, + isNullGroup = false, onGroupClose, onToggleGroup, renderChildComponent, @@ -68,8 +70,11 @@ const GroupPanelComponent = ({ const groupFieldValue = useMemo(() => firstNonNullValue(groupBucket.key), [groupBucket.key]); const groupFilters = useMemo( - () => createGroupFilter(selectedGroup, groupFieldValue), - [groupFieldValue, selectedGroup] + () => + isNullGroup + ? getNullGroupFilter(selectedGroup) + : createGroupFilter(selectedGroup, groupFieldValue), + [groupFieldValue, isNullGroup, selectedGroup] ); const onToggle = useCallback( diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index 625beda320d04..ae1c8b6b0246b 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -17,14 +17,14 @@ import type { Filter } from '@kbn/es-query'; import React, { useMemo, useState } from 'react'; import { METRIC_TYPE, UiCounterMetricType } from '@kbn/analytics'; import { defaultUnit, firstNonNullValue } from '../helpers'; -import { createGroupFilter } from './accordion_panel/helpers'; +import { createGroupFilter, getNullGroupFilter } from './accordion_panel/helpers'; import { GroupPanel } from './accordion_panel'; import { GroupStats } from './accordion_panel/group_stats'; import { EmptyGroupingComponent } from './empty_results_panel'; import { countCss, groupingContainerCss, groupingContainerCssLevel } from './styles'; import { GROUPS_UNIT } from './translations'; import type { GroupingAggregation, GroupPanelRenderer } from './types'; -import { GroupStatsRenderer, OnGroupToggle } from './types'; +import { GroupStatsRenderer, NONE_GROUP_LABEL, OnGroupToggle } from './types'; import { getTelemetryEvent } from '../telemetry/const'; export interface GroupingProps { @@ -80,14 +80,18 @@ const GroupingComponent = ({ ); const unitCount = data?.unitsCount?.value ?? 0; + const nullGroupUnitCount = data?.nullGrouping?.doc_count ?? 0; + const totalUnitCount = unitCount + nullGroupUnitCount; const unitCountText = useMemo(() => { - return `${unitCount.toLocaleString()} ${unit && unit(unitCount)}`; - }, [unitCount, unit]); + return `${totalUnitCount.toLocaleString()} ${unit && unit(totalUnitCount)}`; + }, [totalUnitCount, unit]); const groupCount = data?.groupsCount?.value ?? 0; + const nullGroupCount = nullGroupUnitCount > 0 ? 1 : 0; + const totalGroupCount = groupCount + nullGroupCount; const groupCountText = useMemo( - () => `${groupCount.toLocaleString()} ${GROUPS_UNIT(groupCount)}`, - [groupCount] + () => `${totalGroupCount.toLocaleString()} ${GROUPS_UNIT(totalGroupCount)}`, + [totalGroupCount] ); const groupPanels = useMemo( @@ -159,6 +163,73 @@ const GroupingComponent = ({ trigger, ] ); + + const nullGroupPanels = useMemo(() => { + if (data?.nullGrouping == null || data.nullGrouping.doc_count === 0) return null; + const groupNumber = totalGroupCount; + const group = NONE_GROUP_LABEL; + const groupBucket = { + ...data.nullGrouping, + key: group, + }; + const groupKey = `group-${groupNumber}-${group}`; + return ( + + } + forceState={(trigger[groupKey] && trigger[groupKey].state) ?? 'closed'} + groupBucket={groupBucket} + groupPanelRenderer={groupPanelRenderer && groupPanelRenderer(selectedGroup, groupBucket)} + isLoading={isLoading} + onToggleGroup={(isOpen) => { + // built-in telemetry: UI-counter + tracker?.( + METRIC_TYPE.CLICK, + getTelemetryEvent.groupToggled({ isOpen, groupingId, groupNumber }) + ); + setTrigger({ + // ...trigger, -> this change will keep only one group at a time expanded and one table displayed + [groupKey]: { + state: isOpen ? 'open' : 'closed', + }, + }); + onGroupToggle?.({ isOpen, groupName: group, groupNumber, groupingId }); + }} + renderChildComponent={ + trigger[groupKey] && trigger[groupKey].state === 'open' + ? renderChildComponent + : () => + } + selectedGroup={selectedGroup} + groupingLevel={groupingLevel} + /> + ); + }, [ + data?.nullGrouping, + groupPanelRenderer, + groupStatsRenderer, + groupingId, + groupingLevel, + isLoading, + onGroupClose, + onGroupToggle, + renderChildComponent, + selectedGroup, + takeActionItems, + totalGroupCount, + tracker, + trigger, + ]); + const pageCount = useMemo( () => (groupCount ? Math.ceil(groupCount / itemsPerPage) : 1), [groupCount, itemsPerPage] @@ -174,7 +245,7 @@ const GroupingComponent = ({ style={{ paddingBottom: 20, paddingTop: 20 }} > - {groupCount > 0 && unitCount > 0 ? ( + {totalGroupCount > 0 && totalUnitCount > 0 ? ( @@ -204,9 +275,11 @@ const GroupingComponent = ({ {isLoading && ( )} - {groupCount > 0 ? ( + {totalGroupCount > 0 ? ( <> {groupPanels} + {/* show null grouping on last page only */} + {pageCount === activePage + 1 && nullGroupPanels} {groupCount > 0 && ( <> diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index cf5f55f5c27f3..ea37cf8f042ee 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -14,6 +14,7 @@ export interface GenericBuckets { } export const NONE_GROUP_KEY = 'none'; +export const NONE_GROUP_LABEL = '-'; export type RawBucket = GenericBuckets & T; @@ -23,6 +24,7 @@ export interface RootAggregation { groupByFields?: { buckets?: Array>; }; + nullGrouping?: RawBucket; groupsCount?: { value?: number | null; }; diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 23699c1ccf94a..515601c6bb2cb 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -74,6 +74,23 @@ export const getGroupingQuery = ({ : {}), }, }, + + ...(groupByFields.length > 1 + ? // this happens when group by field is rule name, and all alerts have a rule name + {} + : { + nullGrouping: { + missing: { + field: groupByFields[0], + }, + aggs: { + ...(statsAggregations + ? statsAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {}) + : {}), + }, + }, + }), + ...(rootAggregations ? rootAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {}) : {}), diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index 921a8d3e3d43f..1f348ef10354a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -57,6 +57,7 @@ export const getAlertsGroupingQuery = ({ ], runtimeMappings, size: pageSize, + // sort: [{ unitsCount: { order: 'desc' } }], to, }); From f4812460fcef77a98552aa3f0183d179e39c386f Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 25 Apr 2023 12:31:12 -0600 Subject: [PATCH 03/24] 2nd approach --- .../src/containers/query/helpers.ts | 32 +++++++++++++++++++ .../src/containers/query/index.ts | 9 +++++- .../src/containers/query/types.ts | 1 + .../public/common/lib/kuery/index.ts | 9 ++++++ .../alerts_table/alerts_sub_grouping.tsx | 19 +++++++++-- .../grouping_settings/query_builder.ts | 3 ++ 6 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts new file mode 100644 index 0000000000000..e05f22d789aae --- /dev/null +++ b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts @@ -0,0 +1,32 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ES_FIELD_TYPES } from '@kbn/field-types'; + +export function getFieldTypeMissingValue(esType: string[]) { + const knownType: ES_FIELD_TYPES = esType[0] as ES_FIELD_TYPES; + switch (knownType) { + case ES_FIELD_TYPES.BYTE: + case ES_FIELD_TYPES.DOUBLE: + case ES_FIELD_TYPES.INTEGER: + case ES_FIELD_TYPES.LONG: + case ES_FIELD_TYPES.FLOAT: + case ES_FIELD_TYPES.HALF_FLOAT: + case ES_FIELD_TYPES.SCALED_FLOAT: + case ES_FIELD_TYPES.SHORT: + case ES_FIELD_TYPES.UNSIGNED_LONG: + return 'NaN'; + case ES_FIELD_TYPES.IP: + return '0.0.0.0'; + case ES_FIELD_TYPES.DATE: + case ES_FIELD_TYPES.DATE_NANOS: + return 0; + default: + return '-'; + } +} diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 23699c1ccf94a..ea350d53b8efa 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { getFieldTypeMissingValue } from './helpers'; import type { GroupingQueryArgs, GroupingQuery } from './types'; /** The maximum number of groups to render */ export const DEFAULT_GROUP_BY_FIELD_SIZE = 10; @@ -24,6 +25,7 @@ export const MAX_QUERY_SIZE = 10000; * @param rootAggregations Top level aggregations to get the groups number or overall groups metrics. * Array of {@link NamedAggregation} * @param runtimeMappings mappings of runtime fields [see runtimeMappings]{@link GroupingQueryArgs.runtimeMappings} + * @param selectedGroupEsTypes array of selected group types * @param size number of grouping results per page * @param sort add one or more sorts on specific fields * @param statsAggregations group level aggregations which correspond to {@link GroupStatsRenderer} configuration @@ -35,10 +37,11 @@ export const getGroupingQuery = ({ additionalFilters = [], from, groupByFields, + pageNumber, rootAggregations, runtimeMappings, + selectedGroupEsTypes, size = DEFAULT_GROUP_BY_FIELD_SIZE, - pageNumber, sort, statsAggregations, to, @@ -51,6 +54,8 @@ export const getGroupingQuery = ({ multi_terms: { terms: groupByFields.map((groupByField) => ({ field: groupByField, + // docs with empty field values will be grouped under this key + missing: getFieldTypeMissingValue(selectedGroupEsTypes), })), size: MAX_QUERY_SIZE, }, @@ -58,6 +63,8 @@ export const getGroupingQuery = ({ : { terms: { field: groupByFields[0], + // docs with empty field values will be grouped under this key + missing: getFieldTypeMissingValue(selectedGroupEsTypes), size: MAX_QUERY_SIZE, }, }), diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts index 5a8b2f822fb5c..96a5a73db031c 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts @@ -28,6 +28,7 @@ export interface GroupingQueryArgs { runtimeMappings?: MappingRuntimeFields; additionalAggregationsRoot?: NamedAggregation[]; pageNumber?: number; + selectedGroupEsTypes: string[]; size?: number; sort?: Array<{ [category: string]: { order: 'asc' | 'desc' } }>; statsAggregations?: NamedAggregation[]; diff --git a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts index e21a5a519fc51..e6d5d67daf2bf 100644 --- a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts +++ b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts @@ -97,6 +97,15 @@ export const getBrowserFieldPath = (field: string, browserFields: BrowserFields) return [splitFields[0], 'fields', field]; }; +export const getFieldEsTypes = (field: string, browserFields: BrowserFields): string[] => { + const pathBrowserField = getBrowserFieldPath(field, browserFields); + const browserField = get(pathBrowserField, browserFields); + if (browserField != null) { + return browserField.esTypes; + } + return []; +}; + export const checkIfFieldTypeIsDate = (field: string, browserFields: BrowserFields) => { const pathBrowserField = getBrowserFieldPath(field, browserFields); const browserField = get(pathBrowserField, browserFields); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx index cf8a8128cb166..e6bbceaf77ba5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx @@ -15,7 +15,7 @@ import { getEsQueryConfig } from '@kbn/data-plugin/common'; import type { DynamicGroupingProps } from '@kbn/securitysolution-grouping/src'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; -import { combineQueries } from '../../../common/lib/kuery'; +import { combineQueries, getFieldEsTypes } from '../../../common/lib/kuery'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import type { AlertsGroupingAggregation } from './grouping_settings/types'; import type { Status } from '../../../../common/detection_engine/schemas/common'; @@ -140,17 +140,32 @@ export const GroupedSubLevelComponent: React.FC = ({ } }, [defaultFilters, globalFilters, globalQuery, parentGroupingFilter]); + const selectedGroupEsTypes = useMemo( + () => getFieldEsTypes(selectedGroup, browserFields), + [selectedGroup, browserFields] + ); + const queryGroups = useMemo(() => { return getAlertsGroupingQuery({ additionalFilters, selectedGroup, + selectedGroupEsTypes, from, runtimeMappings, to, pageSize, pageIndex, }); - }, [additionalFilters, from, pageIndex, pageSize, runtimeMappings, selectedGroup, to]); + }, [ + additionalFilters, + from, + pageIndex, + pageSize, + runtimeMappings, + selectedGroup, + selectedGroupEsTypes, + to, + ]); const emptyGlobalQuery = useMemo(() => getGlobalQuery([]), [getGlobalQuery]); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index 921a8d3e3d43f..41830e97a3ad1 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -27,6 +27,7 @@ interface AlertsGroupingQueryParams { pageSize: number; runtimeMappings: MappingRuntimeFields; selectedGroup: string; + selectedGroupEsTypes: string[]; to: string; } @@ -37,6 +38,7 @@ export const getAlertsGroupingQuery = ({ pageSize, runtimeMappings, selectedGroup, + selectedGroupEsTypes, to, }: AlertsGroupingQueryParams) => getGroupingQuery({ @@ -56,6 +58,7 @@ export const getAlertsGroupingQuery = ({ : []), ], runtimeMappings, + selectedGroupEsTypes, size: pageSize, to, }); From c5211aca49837e37dbf32db3272ea774a015d11d Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 25 Apr 2023 12:44:48 -0600 Subject: [PATCH 04/24] add sortf --- .../components/alerts_table/grouping_settings/query_builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index 1f348ef10354a..6bbb4cb9037c4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -57,7 +57,7 @@ export const getAlertsGroupingQuery = ({ ], runtimeMappings, size: pageSize, - // sort: [{ unitsCount: { order: 'desc' } }], + sort: [{ unitsCount: { order: 'desc' } }], to, }); From 0c57652e54c2d31a5cd8341e876e6e31053acc45 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 25 Apr 2023 13:05:11 -0600 Subject: [PATCH 05/24] fixed --- .../src/components/grouping.tsx | 94 +++---------------- .../src/components/types.ts | 7 +- .../src/containers/query/index.ts | 22 +---- .../src/containers/query/types.ts | 3 +- 4 files changed, 23 insertions(+), 103 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index ae1c8b6b0246b..7307ccf97c645 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -24,7 +24,7 @@ import { EmptyGroupingComponent } from './empty_results_panel'; import { countCss, groupingContainerCss, groupingContainerCssLevel } from './styles'; import { GROUPS_UNIT } from './translations'; import type { GroupingAggregation, GroupPanelRenderer } from './types'; -import { GroupStatsRenderer, NONE_GROUP_LABEL, OnGroupToggle } from './types'; +import { GroupStatsRenderer, OnGroupToggle } from './types'; import { getTelemetryEvent } from '../telemetry/const'; export interface GroupingProps { @@ -80,18 +80,14 @@ const GroupingComponent = ({ ); const unitCount = data?.unitsCount?.value ?? 0; - const nullGroupUnitCount = data?.nullGrouping?.doc_count ?? 0; - const totalUnitCount = unitCount + nullGroupUnitCount; const unitCountText = useMemo(() => { - return `${totalUnitCount.toLocaleString()} ${unit && unit(totalUnitCount)}`; - }, [totalUnitCount, unit]); + return `${unitCount.toLocaleString()} ${unit && unit(unitCount)}`; + }, [unitCount, unit]); const groupCount = data?.groupsCount?.value ?? 0; - const nullGroupCount = nullGroupUnitCount > 0 ? 1 : 0; - const totalGroupCount = groupCount + nullGroupCount; const groupCountText = useMemo( - () => `${totalGroupCount.toLocaleString()} ${GROUPS_UNIT(totalGroupCount)}`, - [totalGroupCount] + () => `${groupCount.toLocaleString()} ${GROUPS_UNIT(groupCount)}`, + [groupCount] ); const groupPanels = useMemo( @@ -99,15 +95,21 @@ const GroupingComponent = ({ data?.groupByFields?.buckets?.map((groupBucket, groupNumber) => { const group = firstNonNullValue(groupBucket.key); const groupKey = `group-${groupNumber}-${group}`; + const isNullGroup = groupBucket.nullGroup.doc_count > 0; return ( ({ ] ); - const nullGroupPanels = useMemo(() => { - if (data?.nullGrouping == null || data.nullGrouping.doc_count === 0) return null; - const groupNumber = totalGroupCount; - const group = NONE_GROUP_LABEL; - const groupBucket = { - ...data.nullGrouping, - key: group, - }; - const groupKey = `group-${groupNumber}-${group}`; - return ( - - } - forceState={(trigger[groupKey] && trigger[groupKey].state) ?? 'closed'} - groupBucket={groupBucket} - groupPanelRenderer={groupPanelRenderer && groupPanelRenderer(selectedGroup, groupBucket)} - isLoading={isLoading} - onToggleGroup={(isOpen) => { - // built-in telemetry: UI-counter - tracker?.( - METRIC_TYPE.CLICK, - getTelemetryEvent.groupToggled({ isOpen, groupingId, groupNumber }) - ); - setTrigger({ - // ...trigger, -> this change will keep only one group at a time expanded and one table displayed - [groupKey]: { - state: isOpen ? 'open' : 'closed', - }, - }); - onGroupToggle?.({ isOpen, groupName: group, groupNumber, groupingId }); - }} - renderChildComponent={ - trigger[groupKey] && trigger[groupKey].state === 'open' - ? renderChildComponent - : () => - } - selectedGroup={selectedGroup} - groupingLevel={groupingLevel} - /> - ); - }, [ - data?.nullGrouping, - groupPanelRenderer, - groupStatsRenderer, - groupingId, - groupingLevel, - isLoading, - onGroupClose, - onGroupToggle, - renderChildComponent, - selectedGroup, - takeActionItems, - totalGroupCount, - tracker, - trigger, - ]); - const pageCount = useMemo( () => (groupCount ? Math.ceil(groupCount / itemsPerPage) : 1), [groupCount, itemsPerPage] @@ -245,7 +181,7 @@ const GroupingComponent = ({ style={{ paddingBottom: 20, paddingTop: 20 }} > - {totalGroupCount > 0 && totalUnitCount > 0 ? ( + {groupCount > 0 && unitCount > 0 ? ( @@ -275,11 +211,9 @@ const GroupingComponent = ({ {isLoading && ( )} - {totalGroupCount > 0 ? ( + {groupCount > 0 ? ( <> {groupPanels} - {/* show null grouping on last page only */} - {pageCount === activePage + 1 && nullGroupPanels} {groupCount > 0 && ( <> diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index ea37cf8f042ee..78ac06d1d77e9 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -6,15 +6,13 @@ * Side Public License, v 1. */ -// copied from common/search_strategy/common export interface GenericBuckets { key: string | string[]; key_as_string?: string; // contains, for example, formatted dates doc_count: number; } - +export type MissingAggregation = Record<'nullGroup', GenericBuckets>; export const NONE_GROUP_KEY = 'none'; -export const NONE_GROUP_LABEL = '-'; export type RawBucket = GenericBuckets & T; @@ -22,9 +20,8 @@ export type RawBucket = GenericBuckets & T; // TODO: write developer docs for these fields export interface RootAggregation { groupByFields?: { - buckets?: Array>; + buckets?: Array & MissingAggregation>; }; - nullGrouping?: RawBucket; groupsCount?: { value?: number | null; }; diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index f2d25d2222a2f..5e6523e8aaef9 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -76,28 +76,16 @@ export const getGroupingQuery = ({ size, }, }, + nullGroup: { + missing: { + field: groupByFields[0], + }, + }, ...(statsAggregations ? statsAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {}) : {}), }, }, - - ...(groupByFields.length > 1 - ? // this happens when group by field is rule name, and all alerts have a rule name - {} - : { - nullGrouping: { - missing: { - field: groupByFields[0], - }, - aggs: { - ...(statsAggregations - ? statsAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {}) - : {}), - }, - }, - }), - ...(rootAggregations ? rootAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {}) : {}), diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts index 96a5a73db031c..d7448132d7283 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts @@ -19,6 +19,7 @@ interface RangeAgg { } export type NamedAggregation = Record; +export type MissingAggregation = Record<'nullGroup', estypes.AggregationsAggregationContainer>; export interface GroupingQueryArgs { additionalFilters: BoolAgg[]; @@ -37,7 +38,7 @@ export interface GroupingQueryArgs { export interface MainAggregation extends NamedAggregation { groupByFields: { - aggs: NamedAggregation; + aggs: MissingAggregation & NamedAggregation; multi_terms?: estypes.AggregationsAggregationContainer['multi_terms']; terms?: estypes.AggregationsAggregationContainer['terms']; }; From 0ec5ef61dd3a8b54e700495e029ac3857420bde8 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 25 Apr 2023 19:16:03 +0000 Subject: [PATCH 06/24] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- packages/kbn-securitysolution-grouping/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/kbn-securitysolution-grouping/tsconfig.json b/packages/kbn-securitysolution-grouping/tsconfig.json index 621ba68957cf1..efecb457f96de 100644 --- a/packages/kbn-securitysolution-grouping/tsconfig.json +++ b/packages/kbn-securitysolution-grouping/tsconfig.json @@ -25,6 +25,7 @@ "@kbn/kibana-react-plugin", "@kbn/shared-svg", "@kbn/ui-theme", - "@kbn/analytics" + "@kbn/analytics", + "@kbn/field-types" ] } From 0e3a5275b7c5fe1a0d918fb6d4fbefc8deb7becd Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 25 Apr 2023 13:57:08 -0600 Subject: [PATCH 07/24] messaging for null group --- .../src/components/accordion_panel/index.tsx | 29 +++- .../src/components/grouping.mock.tsx | 2 + .../src/components/grouping.tsx | 9 +- .../src/components/translations.ts | 7 + .../src/components/types.ts | 7 +- .../src/containers/query/index.ts | 2 + .../alerts_table/alerts_grouping.tsx | 1 + .../group_panel_renderers.tsx | 143 ++++++++++-------- 8 files changed, 132 insertions(+), 68 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx index 368957a1335a6..28a64c1455ee1 100644 --- a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiIconTip } from '@elastic/eui'; import type { Filter } from '@kbn/es-query'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { firstNonNullValue } from '../../helpers'; @@ -23,20 +23,34 @@ interface GroupPanelProps { groupingLevel?: number; isLoading: boolean; isNullGroup?: boolean; + nullGroupMessage: string; onGroupClose: () => void; onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket) => void; renderChildComponent: (groupFilter: Filter[]) => React.ReactElement; selectedGroup: string; } -const DefaultGroupPanelRenderer = ({ title }: { title: string }) => ( +const DefaultGroupPanelRenderer = ({ + isNullGroup, + title, + nullGroupMessage, +}: { + isNullGroup: boolean; + title: string; + nullGroupMessage: string; +}) => (
- +

{title}

+ {isNullGroup && ( + + + + )}
); @@ -55,6 +69,7 @@ const GroupPanelComponent = ({ onToggleGroup, renderChildComponent, selectedGroup, + nullGroupMessage, }: GroupPanelProps) => { const lastForceState = useRef(forceState); useEffect(() => { @@ -91,7 +106,13 @@ const GroupPanelComponent = ({ buttonClassName={customAccordionButtonClassName} buttonContent={
- {groupPanelRenderer ?? } + {groupPanelRenderer ?? ( + + )}
} buttonElement="div" diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx index 0647c14485fad..1aaff639c1aca 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx @@ -27,6 +27,7 @@ export const mockGroupingProps = { key: [rule1Name, rule1Desc], key_as_string: `${rule1Name}|${rule1Desc}`, doc_count: 1, + nullGroup: { doc_count: 0 }, hostsCountAggregation: { value: 1, }, @@ -59,6 +60,7 @@ export const mockGroupingProps = { key: [rule2Name, rule2Desc], key_as_string: `${rule2Name}|${rule2Desc}`, doc_count: 1, + nullGroup: { doc_count: 0 }, hostsCountAggregation: { value: 1, }, diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index 7307ccf97c645..71d64ecca5afb 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -22,7 +22,7 @@ import { GroupPanel } from './accordion_panel'; import { GroupStats } from './accordion_panel/group_stats'; import { EmptyGroupingComponent } from './empty_results_panel'; import { countCss, groupingContainerCss, groupingContainerCssLevel } from './styles'; -import { GROUPS_UNIT } from './translations'; +import { GROUPS_UNIT, NULL_GROUP } from './translations'; import type { GroupingAggregation, GroupPanelRenderer } from './types'; import { GroupStatsRenderer, OnGroupToggle } from './types'; import { getTelemetryEvent } from '../telemetry/const'; @@ -96,11 +96,15 @@ const GroupingComponent = ({ const group = firstNonNullValue(groupBucket.key); const groupKey = `group-${groupNumber}-${group}`; const isNullGroup = groupBucket.nullGroup.doc_count > 0; + const nullGroupMessage = isNullGroup + ? NULL_GROUP(selectedGroup, unit(groupBucket.nullGroup.doc_count)) + : ''; return ( ({ forceState={(trigger[groupKey] && trigger[groupKey].state) ?? 'closed'} groupBucket={groupBucket} groupPanelRenderer={ - groupPanelRenderer && groupPanelRenderer(selectedGroup, groupBucket) + groupPanelRenderer && + groupPanelRenderer(selectedGroup, groupBucket, nullGroupMessage) } isLoading={isLoading} onToggleGroup={(isOpen) => { diff --git a/packages/kbn-securitysolution-grouping/src/components/translations.ts b/packages/kbn-securitysolution-grouping/src/components/translations.ts index f2cb8a172dbdc..2209d61d1b34f 100644 --- a/packages/kbn-securitysolution-grouping/src/components/translations.ts +++ b/packages/kbn-securitysolution-grouping/src/components/translations.ts @@ -54,3 +54,10 @@ export const DEFAULT_UNIT = (totalCount: number) => values: { totalCount }, defaultMessage: `{totalCount, plural, =1 {event} other {events}}`, }); + +export const NULL_GROUP = (selectedGroup: string, unit: string) => + i18n.translate('grouping.nullGroup.title', { + values: { selectedGroup, unit }, + defaultMessage: + 'The selected group by field, {selectedGroup}, is missing a value for this group of {unit}.', + }); diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index 78ac06d1d77e9..fa50a3d889b0a 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -6,15 +6,17 @@ * Side Public License, v 1. */ +// copied from common/search_strategy/common export interface GenericBuckets { key: string | string[]; key_as_string?: string; // contains, for example, formatted dates doc_count: number; } -export type MissingAggregation = Record<'nullGroup', GenericBuckets>; + export const NONE_GROUP_KEY = 'none'; export type RawBucket = GenericBuckets & T; +export type MissingAggregation = Record<'nullGroup', { doc_count: number }>; /** Defines the shape of the aggregation returned by Elasticsearch */ // TODO: write developer docs for these fields @@ -59,7 +61,8 @@ export type GroupStatsRenderer = ( export type GroupPanelRenderer = ( selectedGroup: string, - fieldBucket: RawBucket + fieldBucket: RawBucket, + nullGroupMessage: string ) => JSX.Element | undefined; export type OnGroupToggle = (params: { diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 5e6523e8aaef9..5c27c4fff15ff 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -76,6 +76,8 @@ export const getGroupingQuery = ({ size, }, }, + // this agg will return a count when the bucket is of a missing field value + // this is so that we have a flag in the UI for the "missing" group nullGroup: { missing: { field: groupByFields[0], diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index 4e04049553ac6..968e415f38214 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -188,6 +188,7 @@ const GroupedAlertsTableComponent: React.FC = (props) return getLevel( level + 1, selectedGroups[level + 1], + // stringify because if the filter is passed as an object, it will cause unnecessary re-rendering JSON.stringify([ ...groupingFilters, ...(parentGroupingFilter ? JSON.parse(parentGroupingFilter) : []), diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx index ffea45007fc6d..00c0405c31348 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx @@ -11,6 +11,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, + EuiIconTip, EuiText, EuiTextColor, EuiTitle, @@ -18,16 +19,17 @@ import { import { euiThemeVars } from '@kbn/ui-theme'; import { isArray } from 'lodash/fp'; import React from 'react'; -import type { RawBucket } from '@kbn/securitysolution-grouping'; +import type { GroupPanelRenderer } from '@kbn/securitysolution-grouping/src'; import type { AlertsGroupingAggregation } from './types'; import { firstNonNullValue } from '../../../../../common/endpoint/models/ecs_safety_helpers'; import type { GenericBuckets } from '../../../../../common/search_strategy'; import { PopoverItems } from '../../../../common/components/popover_items'; import { COLUMN_TAGS } from '../../../pages/detection_engine/rules/translations'; -export const renderGroupPanel = ( - selectedGroup: string, - bucket: RawBucket +export const renderGroupPanel: GroupPanelRenderer = ( + selectedGroup, + bucket, + nullGroupMessage ) => { switch (selectedGroup) { case 'kibana.alert.rule.name': @@ -39,11 +41,11 @@ export const renderGroupPanel = ( /> ) : undefined; case 'host.name': - return ; + return ; case 'user.name': - return ; + return ; case 'source.ip': - return ; + return ; } }; @@ -89,66 +91,87 @@ const RuleNameGroupContent = React.memo<{ }); RuleNameGroupContent.displayName = 'RuleNameGroup'; -const HostNameGroupContent = React.memo<{ hostName: string | string[] }>(({ hostName }) => ( - - ( + ({ hostName, nullGroupMessage }) => ( + - - - - - -
{hostName}
-
-
-
-)); -HostNameGroupContent.displayName = 'HostNameGroupContent'; - -const UserNameGroupContent = React.memo<{ userName: string | string[] }>(({ userName }) => { - const userNameValue = firstNonNullValue(userName) ?? '-'; - return ( - - - + + - + -
{userName}
+
{hostName}
+ {nullGroupMessage.length > 0 && ( + + + + )}
- ); -}); + ) +); +HostNameGroupContent.displayName = 'HostNameGroupContent'; + +const UserNameGroupContent = React.memo<{ userName: string | string[]; nullGroupMessage: string }>( + ({ userName, nullGroupMessage }) => { + const userNameValue = firstNonNullValue(userName) ?? '-'; + return ( + + + + + + + +
{userName}
+
+
+ {nullGroupMessage.length > 0 && ( + + + + )} +
+ ); + } +); UserNameGroupContent.displayName = 'UserNameGroupContent'; -const SourceIpGroupContent = React.memo<{ sourceIp: string | string[] }>(({ sourceIp }) => ( - - - - - - -
{sourceIp}
-
-
-
-)); +const SourceIpGroupContent = React.memo<{ sourceIp: string | string[]; nullGroupMessage: string }>( + ({ sourceIp, nullGroupMessage }) => ( + + + + + + +
{sourceIp}
+
+
+ {nullGroupMessage.length > 0 && ( + + + + )} +
+ ) +); SourceIpGroupContent.displayName = 'SourceIpGroupContent'; From aaec83a361215c8fe7bccad178e3d05f479258ee Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Tue, 25 Apr 2023 16:58:20 -0600 Subject: [PATCH 08/24] awesome tests --- .../src/components/accordion_panel/index.tsx | 2 +- .../src/components/grouping.mock.tsx | 57 ++++++++++--- .../src/components/grouping.stories.tsx | 2 +- .../src/components/grouping.test.tsx | 34 ++++++-- .../src/components/grouping.tsx | 1 + .../src/containers/query/index.test.ts | 35 +++++++- .../alerts_table/alerts_grouping.test.tsx | 62 ++++++++++++++ .../group_panel_renderers.test.tsx | 83 ++++++++++++------- .../alerts_table/grouping_settings/mock.ts | 50 +++++++++++ 9 files changed, 273 insertions(+), 53 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx index 28a64c1455ee1..497791fe2e211 100644 --- a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx @@ -47,7 +47,7 @@ const DefaultGroupPanelRenderer = ({
{isNullGroup && ( - + )} diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx index 1aaff639c1aca..f11ca4c5c09f0 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx @@ -8,30 +8,28 @@ import React from 'react'; import { EuiContextMenuItem } from '@elastic/eui'; -export const rule1Name = 'Rule 1 name'; -const rule1Desc = 'Rule 1 description'; -export const rule2Name = 'Rule 2 name'; -const rule2Desc = 'Rule 2 description'; +export const host1Name = 'nice-host'; +export const host2Name = 'cool-host'; export const mockGroupingProps = { activePage: 0, data: { groupsCount: { - value: 2, + value: 3, }, groupByFields: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ { - key: [rule1Name, rule1Desc], - key_as_string: `${rule1Name}|${rule1Desc}`, + key: [host1Name], + key_as_string: `${host1Name}`, doc_count: 1, nullGroup: { doc_count: 0 }, hostsCountAggregation: { value: 1, }, - ruleTags: { + hostTags: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [], @@ -57,14 +55,14 @@ export const mockGroupingProps = { }, }, { - key: [rule2Name, rule2Desc], - key_as_string: `${rule2Name}|${rule2Desc}`, + key: [host2Name], + key_as_string: `${host2Name}`, doc_count: 1, nullGroup: { doc_count: 0 }, hostsCountAggregation: { value: 1, }, - ruleTags: { + hostTags: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [], @@ -89,10 +87,43 @@ export const mockGroupingProps = { value: 1, }, }, + { + key: ['-'], + key_as_string: `-`, + doc_count: 11, + nullGroup: { doc_count: 11 }, + hostsCountAggregation: { + value: 11, + }, + hostTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + alertsCount: { + value: 11, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 11, + }, + ], + }, + countSeveritySubAggregation: { + value: 11, + }, + usersCountAggregation: { + value: 11, + }, + }, ], }, unitsCount: { - value: 2, + value: 14, }, }, groupingId: 'test-grouping-id', @@ -100,7 +131,7 @@ export const mockGroupingProps = { itemsPerPage: 25, renderChildComponent: () =>

{'child component'}

, onGroupClose: () => {}, - selectedGroup: 'kibana.alert.rule.name', + selectedGroup: 'host.name', takeActionItems: () => [ {}}> {'Mark as acknowledged'} diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx index b961402ee3a7c..ed08d8541d0e8 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx @@ -23,6 +23,6 @@ export default { }, }; -export const Emtpy: Story = () => { +export const Empty: Story = () => { return ; }; diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx index 2376614ab444c..151b83e45cfd5 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx @@ -10,11 +10,11 @@ import { fireEvent, render, within } from '@testing-library/react'; import React from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import { Grouping } from './grouping'; -import { createGroupFilter } from './accordion_panel/helpers'; +import { createGroupFilter, getNullGroupFilter } from './accordion_panel/helpers'; import { METRIC_TYPE } from '@kbn/analytics'; import { getTelemetryEvent } from '../telemetry/const'; -import { mockGroupingProps, rule1Name, rule2Name } from './grouping.mock'; +import { mockGroupingProps, host1Name, host2Name } from './grouping.mock'; const renderChildComponent = jest.fn(); const takeActionItems = jest.fn(); @@ -37,9 +37,9 @@ describe('grouping container', () => { ); - expect(getByTestId('unit-count').textContent).toBe('2 events'); - expect(getByTestId('group-count').textContent).toBe('2 groups'); - expect(getAllByTestId('grouping-accordion').length).toBe(2); + expect(getByTestId('unit-count').textContent).toBe('14 events'); + expect(getByTestId('group-count').textContent).toBe('3 groups'); + expect(getAllByTestId('grouping-accordion').length).toBe(3); expect(queryByTestId('empty-results-panel')).not.toBeInTheDocument(); }); @@ -79,12 +79,12 @@ describe('grouping container', () => { fireEvent.click(group1); expect(renderChildComponent).toHaveBeenNthCalledWith( 1, - createGroupFilter(testProps.selectedGroup, rule1Name) + createGroupFilter(testProps.selectedGroup, host1Name) ); fireEvent.click(group2); expect(renderChildComponent).toHaveBeenNthCalledWith( 2, - createGroupFilter(testProps.selectedGroup, rule2Name) + createGroupFilter(testProps.selectedGroup, host2Name) ); }); @@ -116,4 +116,24 @@ describe('grouping container', () => { }) ); }); + + it('Renders a null group and passes the correct filter to take actions and child component', () => { + takeActionItems.mockReturnValue([]); + const { getAllByTestId, getByTestId } = render( + + + + ); + expect(getByTestId('null-group-icon')).toBeInTheDocument(); + + let lastGroup = getAllByTestId('grouping-accordion').at(-1); + fireEvent.click(within(lastGroup!).getByTestId('take-action-button')); + + expect(takeActionItems).toHaveBeenCalledWith(getNullGroupFilter('host.name'), 2); + + lastGroup = getAllByTestId('grouping-accordion').at(-1); + fireEvent.click(within(lastGroup!).getByTestId('group-panel-toggle')); + + expect(renderChildComponent).toHaveBeenCalledWith(getNullGroupFilter('host.name')); + }); }); diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index 71d64ecca5afb..fb9c4594c127b 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -168,6 +168,7 @@ const GroupingComponent = ({ takeActionItems, tracker, trigger, + unit, ] ); diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts index c3f81c4071c58..cc2060ed610af 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts @@ -53,6 +53,7 @@ const testProps: GroupingQueryArgs = { pageNumber: 0, rootAggregations: [], runtimeMappings: {}, + selectedGroupEsTypes: ['keyword'], size: 25, to: '2023-02-23T06:59:59.999Z', }; @@ -65,9 +66,11 @@ describe('group selector', () => { expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); expect(result.aggs.groupByFields.terms).toEqual({ field: 'host.name', + missing: '-', size: MAX_QUERY_SIZE, }); expect(result.aggs.groupByFields.aggs).toEqual({ + nullGroup: { missing: { field: 'host.name' } }, bucket_truncate: { bucket_sort: { from: 0, size: 25 } }, alertsCount: { cardinality: { field: 'kibana.alert.uuid' } }, rulesCountAggregation: { cardinality: { field: 'kibana.alert.rule.rule_id' } }, @@ -112,7 +115,10 @@ describe('group selector', () => { }); expect(result.aggs.groupByFields.terms).toBeUndefined(); expect(result.aggs.groupByFields.multi_terms).toEqual({ - terms: [{ field: 'kibana.alert.rule.name' }, { field: 'kibana.alert.rule.description' }], + terms: [ + { field: 'kibana.alert.rule.name', missing: '-' }, + { field: 'kibana.alert.rule.description', missing: '-' }, + ], size: MAX_QUERY_SIZE, }); }); @@ -144,4 +150,31 @@ describe('group selector', () => { }); expect(result.query.bool.filter.length).toEqual(2); }); + it('Uses NaN for number fields', () => { + const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['long'] }); + expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); + expect(result.aggs.groupByFields.terms).toEqual({ + field: 'host.name', + missing: 'NaN', + size: MAX_QUERY_SIZE, + }); + }); + it('Uses 0.0.0.0 for ip fields', () => { + const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['ip'] }); + expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); + expect(result.aggs.groupByFields.terms).toEqual({ + field: 'host.name', + missing: '0.0.0.0', + size: MAX_QUERY_SIZE, + }); + }); + it('Uses 0 for date fields', () => { + const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['date'] }); + expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); + expect(result.aggs.groupByFields.terms).toEqual({ + field: 'host.name', + missing: 0, + size: MAX_QUERY_SIZE, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx index 4a57d7cac8e73..59496508d383a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx @@ -214,6 +214,68 @@ describe('GroupedAlertsTable', () => { expect(within(level0).getByTestId('alerts-table')).toBeInTheDocument(); }); + it('Query gets passed correctly', () => { + jest + .spyOn(window.localStorage, 'getItem') + .mockReturnValue(getMockStorageState(['kibana.alert.rule.name'])); + + render( + + + + ); + expect(mockUseQueryAlerts).toHaveBeenLastCalledWith({ + indexName: 'test', + query: { + _source: false, + aggs: { + groupByFields: { + aggs: { + bucket_truncate: { + bucket_sort: { from: 0, size: 25, sort: [{ unitsCount: { order: 'desc' } }] }, + }, + countSeveritySubAggregation: { cardinality: { field: 'kibana.alert.severity' } }, + hostsCountAggregation: { cardinality: { field: 'host.name' } }, + nullGroup: { missing: { field: 'kibana.alert.rule.name' } }, + ruleTags: { terms: { field: 'kibana.alert.rule.tags' } }, + severitiesSubAggregation: { terms: { field: 'kibana.alert.severity' } }, + unitsCount: { cardinality: { field: 'kibana.alert.uuid' } }, + usersCountAggregation: { cardinality: { field: 'user.name' } }, + }, + multi_terms: { + size: 10000, + terms: [ + { field: 'kibana.alert.rule.name', missing: '-' }, + { field: 'kibana.alert.rule.description', missing: '-' }, + ], + }, + }, + groupsCount: { cardinality: { field: 'kibana.alert.rule.name' } }, + unitsCount: { value_count: { field: 'kibana.alert.rule.name' } }, + }, + query: { + bool: { + filter: [ + { bool: { filter: [], must: [], must_not: [], should: [] } }, + { + range: { + '@timestamp': { + gte: '2020-07-07T08:20:18.966Z', + lte: '2020-07-08T08:20:18.966Z', + }, + }, + }, + ], + }, + }, + runtime_mappings: {}, + size: 0, + }, + queryName: 'securitySolutionUI fetchAlerts grouping', + skip: false, + }); + }); + it('renders grouping table in second accordion level when 2 groups are selected', async () => { jest .spyOn(window.localStorage, 'getItem') diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx index ff70bfe8e47ce..2038bddea952e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.test.tsx @@ -5,51 +5,74 @@ * 2.0. */ -import { shallow } from 'enzyme'; - import { renderGroupPanel } from '.'; +import { render } from '@testing-library/react'; describe('renderGroupPanel', () => { it('renders correctly when the field renderer exists', () => { - const wrapperRuleName = shallow( - renderGroupPanel('kibana.alert.rule.name', { - key: ['Rule name test', 'Some description'], - doc_count: 10, - })! + let { getByTestId } = render( + renderGroupPanel( + 'kibana.alert.rule.name', + { + key: ['Rule name test', 'Some description'], + doc_count: 10, + }, + 'This is a null group!' + )! ); - expect(wrapperRuleName.find('[data-test-subj="rule-name-group-renderer"]')).toBeTruthy(); - const wrapperHostName = shallow( - renderGroupPanel('host.name', { - key: 'Host', - doc_count: 2, - })! + expect(getByTestId('rule-name-group-renderer')).toBeInTheDocument(); + const result1 = render( + renderGroupPanel( + 'host.name', + { + key: 'Host', + doc_count: 2, + }, + 'This is a null group!' + )! ); + getByTestId = result1.getByTestId; + + expect(getByTestId('host-name-group-renderer')).toBeInTheDocument(); - expect(wrapperHostName.find('[data-test-subj="host-name-group-renderer"]')).toBeTruthy(); - const wrapperUserName = shallow( - renderGroupPanel('user.name', { - key: 'User test', - doc_count: 1, - })! + const result2 = render( + renderGroupPanel( + 'user.name', + { + key: 'User test', + doc_count: 1, + }, + 'This is a null group!' + )! ); + getByTestId = result2.getByTestId; - expect(wrapperUserName.find('[data-test-subj="host-name-group-renderer"]')).toBeTruthy(); - const wrapperSourceIp = shallow( - renderGroupPanel('source.ip', { - key: 'sourceIp', - doc_count: 23, - })! + expect(getByTestId('host-name-group-renderer')).toBeInTheDocument(); + const result3 = render( + renderGroupPanel( + 'source.ip', + { + key: 'sourceIp', + doc_count: 23, + }, + 'This is a null group!' + )! ); + getByTestId = result3.getByTestId; - expect(wrapperSourceIp.find('[data-test-subj="source-ip-group-renderer"]')).toBeTruthy(); + expect(getByTestId('source-ip-group-renderer')).toBeInTheDocument(); }); it('returns undefined when the renderer does not exist', () => { - const wrapper = renderGroupPanel('process.name', { - key: 'process', - doc_count: 10, - }); + const wrapper = renderGroupPanel( + 'process.name', + { + key: 'process', + doc_count: 10, + }, + 'This is a null group!' + ); expect(wrapper).toBeUndefined(); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts index 9e0b4e63715aa..6416319a72a31 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts @@ -50,6 +50,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -91,6 +92,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -132,6 +134,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -173,6 +176,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -214,6 +218,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -255,6 +260,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -296,6 +302,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -337,6 +344,7 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -378,6 +386,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -419,6 +428,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -460,6 +470,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -501,6 +512,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -542,6 +554,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -583,6 +596,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -624,6 +638,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -665,6 +680,7 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -706,6 +722,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -747,6 +764,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -788,6 +806,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -829,6 +848,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -870,6 +890,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -911,6 +932,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -952,6 +974,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -993,6 +1016,7 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1034,6 +1058,7 @@ export const groupingSearchResponse = { unitsCount: { value: 91, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1085,6 +1110,7 @@ export const groupingSearchResponse = { unitsCount: { value: 75, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1111,6 +1137,7 @@ export const groupingSearchResponse = { unitsCount: { value: 63, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1137,6 +1164,7 @@ export const groupingSearchResponse = { unitsCount: { value: 51, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1163,6 +1191,7 @@ export const groupingSearchResponse = { unitsCount: { value: 42, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1189,6 +1218,7 @@ export const groupingSearchResponse = { unitsCount: { value: 33, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1215,6 +1245,7 @@ export const groupingSearchResponse = { unitsCount: { value: 30, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1241,6 +1272,7 @@ export const groupingSearchResponse = { unitsCount: { value: 27, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1267,6 +1299,7 @@ export const groupingSearchResponse = { unitsCount: { value: 27, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1293,6 +1326,7 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1319,6 +1353,7 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1345,6 +1380,7 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1371,6 +1407,7 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1397,6 +1434,7 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1423,6 +1461,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1449,6 +1488,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1475,6 +1515,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1501,6 +1542,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1527,6 +1569,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1553,6 +1596,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1579,6 +1623,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1605,6 +1650,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1631,6 +1677,7 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1657,6 +1704,7 @@ export const groupingSearchResponse = { unitsCount: { value: 18, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1683,6 +1731,7 @@ export const groupingSearchResponse = { unitsCount: { value: 18, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1709,6 +1758,7 @@ export const groupingSearchResponse = { unitsCount: { value: 18, }, + nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, From 1cf328b1ab57697f326eed72caf1ca1d88fdaa59 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Wed, 26 Apr 2023 11:49:54 -0600 Subject: [PATCH 09/24] WIP - better query --- .../src/components/grouping.tsx | 18 +++-- .../src/components/types.ts | 8 ++- .../src/containers/query/helpers.ts | 11 ++-- .../src/containers/query/index.test.ts | 56 +++++++++++++--- .../src/containers/query/index.ts | 66 +++++++++++-------- .../src/containers/query/types.ts | 8 +-- .../alerts_table/alerts_sub_grouping.tsx | 25 +++++-- .../group_panel_renderers.tsx | 4 +- .../grouping_settings/query_builder.ts | 18 ++--- .../alerts_table/grouping_settings/types.ts | 3 + 10 files changed, 146 insertions(+), 71 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index fb9c4594c127b..80d3e5de750cf 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -78,13 +78,20 @@ const GroupingComponent = ({ const [trigger, setTrigger] = useState>( {} ); + const [nullCount, setNullCount] = useState({ unit: 0, group: 0 }); - const unitCount = data?.unitsCount?.value ?? 0; + const unitCount = useMemo( + () => (data?.unitsCount?.value ?? 0) + nullCount.unit, + [data?.unitsCount?.value, nullCount.unit] + ); const unitCountText = useMemo(() => { return `${unitCount.toLocaleString()} ${unit && unit(unitCount)}`; }, [unitCount, unit]); - const groupCount = data?.groupsCount?.value ?? 0; + const groupCount = useMemo( + () => (data?.groupsCount?.value ?? 0) + nullCount.group, + [data?.groupsCount?.value, nullCount.group] + ); const groupCountText = useMemo( () => `${groupCount.toLocaleString()} ${GROUPS_UNIT(groupCount)}`, [groupCount] @@ -95,10 +102,13 @@ const GroupingComponent = ({ data?.groupByFields?.buckets?.map((groupBucket, groupNumber) => { const group = firstNonNullValue(groupBucket.key); const groupKey = `group-${groupNumber}-${group}`; - const isNullGroup = groupBucket.nullGroup.doc_count > 0; + const isNullGroup = groupBucket.isNullGroup ?? false; const nullGroupMessage = isNullGroup - ? NULL_GROUP(selectedGroup, unit(groupBucket.nullGroup.doc_count)) + ? NULL_GROUP(selectedGroup, unit(groupBucket.doc_count)) : ''; + if (isNullGroup) { + setNullCount({ unit: groupBucket.doc_count, group: 1 }); + } return ( diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index fa50a3d889b0a..ae82f130e41c9 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -12,17 +12,19 @@ export interface GenericBuckets { key_as_string?: string; // contains, for example, formatted dates doc_count: number; } - export const NONE_GROUP_KEY = 'none'; export type RawBucket = GenericBuckets & T; -export type MissingAggregation = Record<'nullGroup', { doc_count: number }>; + +export interface GroupingBucket { + isNullGroup?: boolean; +} /** Defines the shape of the aggregation returned by Elasticsearch */ // TODO: write developer docs for these fields export interface RootAggregation { groupByFields?: { - buckets?: Array & MissingAggregation>; + buckets?: Array & GroupingBucket>; }; groupsCount?: { value?: number | null; diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts index e05f22d789aae..505253473c578 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts @@ -8,7 +8,7 @@ import { ES_FIELD_TYPES } from '@kbn/field-types'; -export function getFieldTypeMissingValue(esType: string[]) { +export function getFieldTypeMissingValues(esType: string[]) { const knownType: ES_FIELD_TYPES = esType[0] as ES_FIELD_TYPES; switch (knownType) { case ES_FIELD_TYPES.BYTE: @@ -20,13 +20,12 @@ export function getFieldTypeMissingValue(esType: string[]) { case ES_FIELD_TYPES.SCALED_FLOAT: case ES_FIELD_TYPES.SHORT: case ES_FIELD_TYPES.UNSIGNED_LONG: - return 'NaN'; - case ES_FIELD_TYPES.IP: - return '0.0.0.0'; case ES_FIELD_TYPES.DATE: case ES_FIELD_TYPES.DATE_NANOS: - return 0; + return [0, 1]; + case ES_FIELD_TYPES.IP: + return ['0.0.0.0', '::']; default: - return '-'; + return ['-', '--']; } } diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts index cc2060ed610af..4c9be92916018 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts @@ -7,12 +7,12 @@ */ import type { GroupingQueryArgs } from './types'; -import { getGroupingQuery, MAX_QUERY_SIZE } from '.'; +import { getGroupingQuery, MAX_QUERY_SIZE, parseGroupingQuery } from '.'; const testProps: GroupingQueryArgs = { additionalFilters: [], from: '2022-12-28T15:35:32.871Z', - groupByFields: ['host.name'], + groupByField: 'host.name', statsAggregations: [ { alertsCount: { @@ -61,16 +61,15 @@ describe('group selector', () => { beforeEach(() => { jest.clearAllMocks(); }); - it('Sets terms query when single stackBy field requested', () => { + it.only('Sets terms query with missing argument for ', () => { const result = getGroupingQuery(testProps); - expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); - expect(result.aggs.groupByFields.terms).toEqual({ - field: 'host.name', - missing: '-', - size: MAX_QUERY_SIZE, + result.aggs.groupByFields?.multi_terms?.terms.forEach((term, i) => { + expect(term).toEqual({ + field: 'host.name', + missing: i === 0 ? '-' : '--', + }); }); expect(result.aggs.groupByFields.aggs).toEqual({ - nullGroup: { missing: { field: 'host.name' } }, bucket_truncate: { bucket_sort: { from: 0, size: 25 } }, alertsCount: { cardinality: { field: 'kibana.alert.uuid' } }, rulesCountAggregation: { cardinality: { field: 'kibana.alert.rule.rule_id' } }, @@ -177,4 +176,43 @@ describe('group selector', () => { size: MAX_QUERY_SIZE, }); }); + + it('parseGroupingQuery finds and flags the null group', () => { + const data = [ + { + key: ['20.80.64.28', '20.80.64.28'], + key_as_string: '20.80.64.28|20.80.64.28', + doc_count: 75, + }, + { + key: ['0.0.0.0', '0.0.0.0'], + key_as_string: '0.0.0.0|0.0.0.0', + doc_count: 75, + }, + { + key: ['0.0.0.0', '::'], + key_as_string: '0.0.0.0|::', + doc_count: 75, + }, + ]; + const result = parseGroupingQuery(data); + expect(result).toEqual([ + { + key: ['20.80.64.28'], + key_as_string: '20.80.64.28', + doc_count: 75, + }, + { + key: ['0.0.0.0'], + key_as_string: '0.0.0.0', + doc_count: 75, + }, + { + key: ['-'], + key_as_string: '-', + isNullGroup: true, + doc_count: 75, + }, + ]); + }); }); diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 5c27c4fff15ff..b02ffd7d67b84 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -6,7 +6,8 @@ * Side Public License, v 1. */ -import { getFieldTypeMissingValue } from './helpers'; +import { GroupingBucket } from '../..'; +import { RawBucket } from '../../..'; import type { GroupingQueryArgs, GroupingQuery } from './types'; /** The maximum number of groups to render */ export const DEFAULT_GROUP_BY_FIELD_SIZE = 10; @@ -15,6 +16,8 @@ export const DEFAULT_GROUP_BY_FIELD_SIZE = 10; // https://github.com/elastic/kibana/issues/151913 export const MAX_QUERY_SIZE = 10000; +// @ts-ignore +// @ts-ignore /** * Composes grouping query and aggregations * @param additionalFilters Global filtering applicable to the grouping component. @@ -33,10 +36,11 @@ export const MAX_QUERY_SIZE = 10000; * * @returns query dsl {@link GroupingQuery} */ + export const getGroupingQuery = ({ additionalFilters = [], from, - groupByFields, + groupByField, pageNumber, rootAggregations, runtimeMappings, @@ -49,25 +53,19 @@ export const getGroupingQuery = ({ size: 0, aggs: { groupByFields: { - ...(groupByFields.length > 1 - ? { - multi_terms: { - terms: groupByFields.map((groupByField) => ({ - field: groupByField, - // docs with empty field values will be grouped under this key - missing: getFieldTypeMissingValue(selectedGroupEsTypes), - })), - size: MAX_QUERY_SIZE, - }, - } - : { - terms: { - field: groupByFields[0], - // docs with empty field values will be grouped under this key - missing: getFieldTypeMissingValue(selectedGroupEsTypes), - size: MAX_QUERY_SIZE, - }, - }), + multi_terms: { + terms: [ + { + field: groupByField, + missing: '-', + }, + { + field: groupByField, + missing: '--', + }, + ], + size: MAX_QUERY_SIZE, + }, aggs: { bucket_truncate: { bucket_sort: { @@ -76,13 +74,6 @@ export const getGroupingQuery = ({ size, }, }, - // this agg will return a count when the bucket is of a missing field value - // this is so that we have a flag in the UI for the "missing" group - nullGroup: { - missing: { - field: groupByFields[0], - }, - }, ...(statsAggregations ? statsAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {}) : {}), @@ -110,3 +101,22 @@ export const getGroupingQuery = ({ runtime_mappings: runtimeMappings, _source: false, }); + +export const parseGroupingQuery = ( + buckets: Array> +): Array & GroupingBucket> => + buckets.map((group) => { + const groupKeyArray = Array.isArray(group.key) ? group.key : [group.key]; + return groupKeyArray[0] === groupKeyArray[1] + ? { + ...group, + key: [groupKeyArray[0]], + key_as_string: groupKeyArray[0], + } + : { + ...group, + key: ['-'], + key_as_string: '-', + isNullGroup: true, + }; + }); diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts index d7448132d7283..4a370094d0d5c 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/types.ts @@ -19,12 +19,11 @@ interface RangeAgg { } export type NamedAggregation = Record; -export type MissingAggregation = Record<'nullGroup', estypes.AggregationsAggregationContainer>; export interface GroupingQueryArgs { additionalFilters: BoolAgg[]; from: string; - groupByFields: string[]; + groupByField: string; rootAggregations?: NamedAggregation[]; runtimeMappings?: MappingRuntimeFields; additionalAggregationsRoot?: NamedAggregation[]; @@ -38,9 +37,8 @@ export interface GroupingQueryArgs { export interface MainAggregation extends NamedAggregation { groupByFields: { - aggs: MissingAggregation & NamedAggregation; - multi_terms?: estypes.AggregationsAggregationContainer['multi_terms']; - terms?: estypes.AggregationsAggregationContainer['terms']; + aggs: NamedAggregation; + multi_terms: estypes.AggregationsAggregationContainer['multi_terms']; }; } diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx index e6bbceaf77ba5..6fefc30e556cc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx @@ -15,6 +15,7 @@ import { getEsQueryConfig } from '@kbn/data-plugin/common'; import type { DynamicGroupingProps } from '@kbn/securitysolution-grouping/src'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; +import { parseGroupingQuery } from '@kbn/securitysolution-grouping/src'; import { combineQueries, getFieldEsTypes } from '../../../common/lib/kuery'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import type { AlertsGroupingAggregation } from './grouping_settings/types'; @@ -192,6 +193,11 @@ export const GroupedSubLevelComponent: React.FC = ({ skip: isNoneGroup([selectedGroup]), }); + const buckets = useMemo( + () => parseGroupingQuery(alertsGroupsData?.aggregations?.groupByFields?.buckets ?? []), + [alertsGroupsData] + ); + useEffect(() => { if (!isNoneGroup([selectedGroup])) { setAlertsQuery(queryGroups); @@ -240,7 +246,13 @@ export const GroupedSubLevelComponent: React.FC = ({ () => getGrouping({ activePage: pageIndex, - data: alertsGroupsData?.aggregations, + data: { + ...alertsGroupsData?.aggregations, + groupByFields: { + ...alertsGroupsData?.aggregations?.groupByFields, + buckets, + }, + }, groupingLevel, inspectButton: inspect, isLoading: loading || isLoadingGroups, @@ -253,20 +265,21 @@ export const GroupedSubLevelComponent: React.FC = ({ takeActionItems: getTakeActionItems, }), [ - alertsGroupsData?.aggregations, getGrouping, - getTakeActionItems, + pageIndex, + alertsGroupsData, + buckets, groupingLevel, inspect, - isLoadingGroups, loading, - pageIndex, + isLoadingGroups, pageSize, renderChildComponent, onGroupClose, selectedGroup, - setPageIndex, + getTakeActionItems, setPageSize, + setPageIndex, ] ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx index 00c0405c31348..1efa20649845c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx @@ -36,7 +36,9 @@ export const renderGroupPanel: GroupPanelRenderer = ( return isArray(bucket.key) ? ( ) : undefined; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index a4593ef18d48d..cde272af8d498 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -10,14 +10,6 @@ import type { BoolQuery } from '@kbn/es-query'; import type { NamedAggregation } from '@kbn/securitysolution-grouping'; import { isNoneGroup, getGroupingQuery } from '@kbn/securitysolution-grouping'; -const getGroupFields = (groupValue: string) => { - if (groupValue === 'kibana.alert.rule.name') { - return [groupValue, 'kibana.alert.rule.description']; - } else { - return [groupValue]; - } -}; - interface AlertsGroupingQueryParams { additionalFilters: Array<{ bool: BoolQuery; @@ -44,7 +36,7 @@ export const getAlertsGroupingQuery = ({ getGroupingQuery({ additionalFilters, from, - groupByFields: !isNoneGroup([selectedGroup]) ? getGroupFields(selectedGroup) : [], + groupByField: selectedGroup, statsAggregations: !isNoneGroup([selectedGroup]) ? getAggregationsByGroupField(selectedGroup) : [], @@ -78,6 +70,14 @@ const getAggregationsByGroupField = (field: string): NamedAggregation[] => { case 'kibana.alert.rule.name': aggMetrics.push( ...[ + { + description: { + terms: { + field: 'kibana.alert.rule.description', + size: 1, + }, + }, + }, { countSeveritySubAggregation: { cardinality: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts index 72d3d95b86789..d271551f83460 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts @@ -12,6 +12,9 @@ export interface AlertsGroupingAggregation { unitsCount?: { value?: NumberOrNull; }; + description?: { + buckets?: GenericBuckets[]; + }; severitiesSubAggregation?: { buckets?: GenericBuckets[]; }; From 967d70e39f9bc6e35d37822f768827de3c34cfd7 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Wed, 26 Apr 2023 13:05:47 -0600 Subject: [PATCH 10/24] tests --- .../src/components/accordion_panel/index.tsx | 6 +-- .../src/components/grouping.mock.tsx | 8 ++-- .../src/containers/query/helpers.ts | 2 +- .../src/containers/query/index.ts | 10 ++++- .../alerts_table/alerts_grouping.test.tsx | 4 +- .../grouping_settings/query_builder.test.ts | 41 ++++++++++++++++--- 6 files changed, 53 insertions(+), 18 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx index 497791fe2e211..f40fc79eb8c80 100644 --- a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx @@ -23,7 +23,7 @@ interface GroupPanelProps { groupingLevel?: number; isLoading: boolean; isNullGroup?: boolean; - nullGroupMessage: string; + nullGroupMessage?: string; onGroupClose: () => void; onToggleGroup?: (isOpen: boolean, groupBucket: RawBucket) => void; renderChildComponent: (groupFilter: Filter[]) => React.ReactElement; @@ -37,7 +37,7 @@ const DefaultGroupPanelRenderer = ({ }: { isNullGroup: boolean; title: string; - nullGroupMessage: string; + nullGroupMessage?: string; }) => (
@@ -46,7 +46,7 @@ const DefaultGroupPanelRenderer = ({

{title}

- {isNullGroup && ( + {isNullGroup && nullGroupMessage && ( diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx index f11ca4c5c09f0..412644910db99 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx @@ -15,7 +15,7 @@ export const mockGroupingProps = { activePage: 0, data: { groupsCount: { - value: 3, + value: 2, }, groupByFields: { doc_count_error_upper_bound: 0, @@ -25,7 +25,6 @@ export const mockGroupingProps = { key: [host1Name], key_as_string: `${host1Name}`, doc_count: 1, - nullGroup: { doc_count: 0 }, hostsCountAggregation: { value: 1, }, @@ -58,7 +57,6 @@ export const mockGroupingProps = { key: [host2Name], key_as_string: `${host2Name}`, doc_count: 1, - nullGroup: { doc_count: 0 }, hostsCountAggregation: { value: 1, }, @@ -90,8 +88,8 @@ export const mockGroupingProps = { { key: ['-'], key_as_string: `-`, + isNullGroup: true, doc_count: 11, - nullGroup: { doc_count: 11 }, hostsCountAggregation: { value: 11, }, @@ -123,7 +121,7 @@ export const mockGroupingProps = { ], }, unitsCount: { - value: 14, + value: 3, }, }, groupingId: 'test-grouping-id', diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts index 505253473c578..bd7df712bd9f1 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts @@ -8,7 +8,7 @@ import { ES_FIELD_TYPES } from '@kbn/field-types'; -export function getFieldTypeMissingValues(esType: string[]) { +export function getFieldTypeMissingValues(esType: string[]): [number, number] | [string, string] { const knownType: ES_FIELD_TYPES = esType[0] as ES_FIELD_TYPES; switch (knownType) { case ES_FIELD_TYPES.BYTE: diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index b02ffd7d67b84..ec4fba2911b5d 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { getFieldTypeMissingValues } from './helpers'; import { GroupingBucket } from '../..'; import { RawBucket } from '../../..'; import type { GroupingQueryArgs, GroupingQuery } from './types'; @@ -57,11 +58,16 @@ export const getGroupingQuery = ({ terms: [ { field: groupByField, - missing: '-', + // the AggregationsMultiTermLookup type is wrong in the elasticsearch node package + // see linked slack thread to find out when we can remove these ts ignores + // https://urle.me/eAq + // @ts-ignore + missing: getFieldTypeMissingValues(selectedGroupEsTypes)[0], }, { field: groupByField, - missing: '--', + // @ts-ignore + missing: getFieldTypeMissingValues(selectedGroupEsTypes)[1], }, ], size: MAX_QUERY_SIZE, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx index 59496508d383a..fb57db3555628 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx @@ -236,7 +236,7 @@ describe('GroupedAlertsTable', () => { }, countSeveritySubAggregation: { cardinality: { field: 'kibana.alert.severity' } }, hostsCountAggregation: { cardinality: { field: 'host.name' } }, - nullGroup: { missing: { field: 'kibana.alert.rule.name' } }, + description: { terms: { field: 'kibana.alert.rule.description', size: 1 } }, ruleTags: { terms: { field: 'kibana.alert.rule.tags' } }, severitiesSubAggregation: { terms: { field: 'kibana.alert.severity' } }, unitsCount: { cardinality: { field: 'kibana.alert.uuid' } }, @@ -246,7 +246,7 @@ describe('GroupedAlertsTable', () => { size: 10000, terms: [ { field: 'kibana.alert.rule.name', missing: '-' }, - { field: 'kibana.alert.rule.description', missing: '-' }, + { field: 'kibana.alert.rule.name', missing: '--' }, ], }, }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts index b4f8568da2c60..0d92882d4c53a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.test.ts @@ -15,6 +15,7 @@ describe('getAlertsGroupingQuery', () => { pageIndex: 0, pageSize: 25, runtimeMappings: {}, + selectedGroupEsTypes: ['keyword'], selectedGroup: 'kibana.alert.rule.name', additionalFilters: [ { @@ -60,11 +61,23 @@ describe('getAlertsGroupingQuery', () => { field: 'kibana.alert.uuid', }, }, + description: { + terms: { + field: 'kibana.alert.rule.description', + size: 1, + }, + }, bucket_truncate: { bucket_sort: { from: 0, size: 25, - sort: undefined, + sort: [ + { + unitsCount: { + order: 'desc', + }, + }, + ], }, }, countSeveritySubAggregation: { @@ -98,9 +111,11 @@ describe('getAlertsGroupingQuery', () => { terms: [ { field: 'kibana.alert.rule.name', + missing: '-', }, { - field: 'kibana.alert.rule.description', + field: 'kibana.alert.rule.name', + missing: '--', }, ], }, @@ -152,6 +167,7 @@ describe('getAlertsGroupingQuery', () => { pageIndex: 0, pageSize: 25, runtimeMappings: {}, + selectedGroupEsTypes: ['keyword'], selectedGroup: 'process.name', additionalFilters: [ { @@ -201,7 +217,13 @@ describe('getAlertsGroupingQuery', () => { bucket_sort: { from: 0, size: 25, - sort: undefined, + sort: [ + { + unitsCount: { + order: 'desc', + }, + }, + ], }, }, rulesCountAggregation: { @@ -210,9 +232,18 @@ describe('getAlertsGroupingQuery', () => { }, }, }, - terms: { - field: 'process.name', + multi_terms: { size: 10000, + terms: [ + { + field: 'process.name', + missing: '-', + }, + { + field: 'process.name', + missing: '--', + }, + ], }, }, }, From b08fb5f73e9c1374e641c07fed7b7042188161aa Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Wed, 26 Apr 2023 13:08:30 -0600 Subject: [PATCH 11/24] better --- .../src/containers/query/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index ec4fba2911b5d..9d197c774a366 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -17,8 +17,6 @@ export const DEFAULT_GROUP_BY_FIELD_SIZE = 10; // https://github.com/elastic/kibana/issues/151913 export const MAX_QUERY_SIZE = 10000; -// @ts-ignore -// @ts-ignore /** * Composes grouping query and aggregations * @param additionalFilters Global filtering applicable to the grouping component. @@ -108,6 +106,11 @@ export const getGroupingQuery = ({ _source: false, }); +/** + * Parses the grouping query response to add the isNullGroup + * flag to the buckets and to format the bucket keys + * @param buckets buckets returned from the grouping query + */ export const parseGroupingQuery = ( buckets: Array> ): Array & GroupingBucket> => From d029486bc73fb6f7f2bae3681ac2b09274897ea8 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Wed, 26 Apr 2023 13:14:10 -0600 Subject: [PATCH 12/24] better --- .../src/components/grouping.tsx | 2 +- .../src/components/types.ts | 2 +- .../grouping_settings/group_panel_renderers.tsx | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx index 80d3e5de750cf..8182d204f81dd 100644 --- a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx +++ b/packages/kbn-securitysolution-grouping/src/components/grouping.tsx @@ -105,7 +105,7 @@ const GroupingComponent = ({ const isNullGroup = groupBucket.isNullGroup ?? false; const nullGroupMessage = isNullGroup ? NULL_GROUP(selectedGroup, unit(groupBucket.doc_count)) - : ''; + : undefined; if (isNullGroup) { setNullCount({ unit: groupBucket.doc_count, group: 1 }); } diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-securitysolution-grouping/src/components/types.ts index ae82f130e41c9..3ed5cc43ff877 100644 --- a/packages/kbn-securitysolution-grouping/src/components/types.ts +++ b/packages/kbn-securitysolution-grouping/src/components/types.ts @@ -64,7 +64,7 @@ export type GroupStatsRenderer = ( export type GroupPanelRenderer = ( selectedGroup: string, fieldBucket: RawBucket, - nullGroupMessage: string + nullGroupMessage?: string ) => JSX.Element | undefined; export type OnGroupToggle = (params: { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx index 1efa20649845c..2781b78247cf6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx @@ -93,7 +93,7 @@ const RuleNameGroupContent = React.memo<{ }); RuleNameGroupContent.displayName = 'RuleNameGroup'; -const HostNameGroupContent = React.memo<{ hostName: string | string[]; nullGroupMessage: string }>( +const HostNameGroupContent = React.memo<{ hostName: string | string[]; nullGroupMessage?: string }>( ({ hostName, nullGroupMessage }) => ( {hostName} - {nullGroupMessage.length > 0 && ( + {nullGroupMessage && ( @@ -126,7 +126,7 @@ const HostNameGroupContent = React.memo<{ hostName: string | string[]; nullGroup ); HostNameGroupContent.displayName = 'HostNameGroupContent'; -const UserNameGroupContent = React.memo<{ userName: string | string[]; nullGroupMessage: string }>( +const UserNameGroupContent = React.memo<{ userName: string | string[]; nullGroupMessage?: string }>( ({ userName, nullGroupMessage }) => { const userNameValue = firstNonNullValue(userName) ?? '-'; return ( @@ -140,7 +140,7 @@ const UserNameGroupContent = React.memo<{ userName: string | string[]; nullGroup
{userName}
- {nullGroupMessage.length > 0 && ( + {nullGroupMessage && ( @@ -151,7 +151,7 @@ const UserNameGroupContent = React.memo<{ userName: string | string[]; nullGroup ); UserNameGroupContent.displayName = 'UserNameGroupContent'; -const SourceIpGroupContent = React.memo<{ sourceIp: string | string[]; nullGroupMessage: string }>( +const SourceIpGroupContent = React.memo<{ sourceIp: string | string[]; nullGroupMessage?: string }>( ({ sourceIp, nullGroupMessage }) => ( {sourceIp} - {nullGroupMessage.length > 0 && ( + {nullGroupMessage && ( From deb7a03c23d87c7d8c359b64b472698cc9147526 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Wed, 26 Apr 2023 13:21:07 -0600 Subject: [PATCH 13/24] fix --- .../src/containers/query/index.test.ts | 53 ++++++------------- .../src/containers/query/index.ts | 6 +-- 2 files changed, 18 insertions(+), 41 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts index 4c9be92916018..2502dbf8854fc 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts @@ -7,7 +7,7 @@ */ import type { GroupingQueryArgs } from './types'; -import { getGroupingQuery, MAX_QUERY_SIZE, parseGroupingQuery } from '.'; +import { getGroupingQuery, parseGroupingQuery } from '.'; const testProps: GroupingQueryArgs = { additionalFilters: [], @@ -61,7 +61,7 @@ describe('group selector', () => { beforeEach(() => { jest.clearAllMocks(); }); - it.only('Sets terms query with missing argument for ', () => { + it('Sets multi terms query with missing argument for 2 default values', () => { const result = getGroupingQuery(testProps); result.aggs.groupByFields?.multi_terms?.terms.forEach((term, i) => { expect(term).toEqual({ @@ -81,7 +81,7 @@ describe('group selector', () => { expect(result.aggs.groupsNumber).toBeUndefined(); expect(result.query.bool.filter.length).toEqual(1); }); - it('Sets terms query when single stackBy field requested additionalAggregationsRoot', () => { + it('Sets additional rootAggregations', () => { const result = getGroupingQuery({ ...testProps, rootAggregations: [ @@ -107,20 +107,6 @@ describe('group selector', () => { }); expect(result.aggs.groupsNumber).toEqual({ cardinality: { field: 'host.name' } }); }); - it('Sets terms query when multiple stackBy fields requested', () => { - const result = getGroupingQuery({ - ...testProps, - groupByFields: ['kibana.alert.rule.name', 'kibana.alert.rule.description'], - }); - expect(result.aggs.groupByFields.terms).toBeUndefined(); - expect(result.aggs.groupByFields.multi_terms).toEqual({ - terms: [ - { field: 'kibana.alert.rule.name', missing: '-' }, - { field: 'kibana.alert.rule.description', missing: '-' }, - ], - size: MAX_QUERY_SIZE, - }); - }); it('Additional filters get added to the query', () => { const result = getGroupingQuery({ ...testProps, @@ -149,31 +135,22 @@ describe('group selector', () => { }); expect(result.query.bool.filter.length).toEqual(2); }); - it('Uses NaN for number fields', () => { + it('Uses 0/1 for number fields', () => { const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['long'] }); - expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); - expect(result.aggs.groupByFields.terms).toEqual({ - field: 'host.name', - missing: 'NaN', - size: MAX_QUERY_SIZE, + result.aggs.groupByFields?.multi_terms?.terms.forEach((term, i) => { + expect(term).toEqual({ + field: 'host.name', + missing: i === 0 ? 0 : 1, + }); }); }); - it('Uses 0.0.0.0 for ip fields', () => { + it('Uses 0.0.0.0/:: for ip fields', () => { const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['ip'] }); - expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); - expect(result.aggs.groupByFields.terms).toEqual({ - field: 'host.name', - missing: '0.0.0.0', - size: MAX_QUERY_SIZE, - }); - }); - it('Uses 0 for date fields', () => { - const result = getGroupingQuery({ ...testProps, selectedGroupEsTypes: ['date'] }); - expect(result.aggs.groupByFields.multi_terms).toBeUndefined(); - expect(result.aggs.groupByFields.terms).toEqual({ - field: 'host.name', - missing: 0, - size: MAX_QUERY_SIZE, + result.aggs.groupByFields?.multi_terms?.terms.forEach((term, i) => { + expect(term).toEqual({ + field: 'host.name', + missing: i === 0 ? '0.0.0.0' : '::', + }); }); }); diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 9d197c774a366..716db5aca74a3 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -57,14 +57,14 @@ export const getGroupingQuery = ({ { field: groupByField, // the AggregationsMultiTermLookup type is wrong in the elasticsearch node package - // see linked slack thread to find out when we can remove these ts ignores + // see linked slack thread to find out when we can remove these ts expect errors // https://urle.me/eAq - // @ts-ignore + // @ts-expect-error missing: getFieldTypeMissingValues(selectedGroupEsTypes)[0], }, { field: groupByField, - // @ts-ignore + // @ts-expect-error missing: getFieldTypeMissingValues(selectedGroupEsTypes)[1], }, ], From f7a90c986486227e1d332c0f7df614d5c6c4cc31 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Wed, 26 Apr 2023 13:24:51 -0600 Subject: [PATCH 14/24] add comment --- .../kbn-securitysolution-grouping/src/containers/query/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 716db5aca74a3..63f9301b35185 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -54,6 +54,7 @@ export const getGroupingQuery = ({ groupByFields: { multi_terms: { terms: [ + // by looking up multiple missing values, we can ensure we're not overwriting an existing group with the default value { field: groupByField, // the AggregationsMultiTermLookup type is wrong in the elasticsearch node package From 89d3a98fa307e35c32ba73f5764d10790444279b Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 08:20:25 -0600 Subject: [PATCH 15/24] Sergi comment 1 Co-authored-by: Sergi Massaneda --- .../src/containers/query/helpers.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts index bd7df712bd9f1..4ecf935b98760 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts @@ -7,7 +7,11 @@ */ import { ES_FIELD_TYPES } from '@kbn/field-types'; - +/** + * Returns a tuple of values according to the `esType` param, these values are meant to be applied in the _missing_ + * property of the query aggregation of the grouping, to look up for missing values in the response buckets. + * These values do not need to be anything in particular, the only requirement is they have to be 2 different values that validate against the field type. + */ export function getFieldTypeMissingValues(esType: string[]): [number, number] | [string, string] { const knownType: ES_FIELD_TYPES = esType[0] as ES_FIELD_TYPES; switch (knownType) { From dbed7a92a8a6fed8dae62b57c5214f3b693384f5 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 08:20:35 -0600 Subject: [PATCH 16/24] Sergi comment 2 Co-authored-by: Sergi Massaneda --- .../kbn-securitysolution-grouping/src/containers/query/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 63f9301b35185..6c5e45225ce4e 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -117,6 +117,8 @@ export const parseGroupingQuery = ( ): Array & GroupingBucket> => buckets.map((group) => { const groupKeyArray = Array.isArray(group.key) ? group.key : [group.key]; + // If the keys are different means that the `missing` values of the multi_terms aggregation have been applied, we use the default empty string. + // If the keys are equal means the `missing` values have not been applied, they are stored values. return groupKeyArray[0] === groupKeyArray[1] ? { ...group, From 7f97490b777060afd828c30253a270f77af1e4ba Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 08:20:48 -0600 Subject: [PATCH 17/24] Sergi fix 1 Co-authored-by: Sergi Massaneda --- .../src/containers/query/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 6c5e45225ce4e..b00a46a33fefd 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -116,7 +116,9 @@ export const parseGroupingQuery = ( buckets: Array> ): Array & GroupingBucket> => buckets.map((group) => { - const groupKeyArray = Array.isArray(group.key) ? group.key : [group.key]; + if (!Array.isArray(group.key)) { + return group; + } // If the keys are different means that the `missing` values of the multi_terms aggregation have been applied, we use the default empty string. // If the keys are equal means the `missing` values have not been applied, they are stored values. return groupKeyArray[0] === groupKeyArray[1] From 7c8ac3a05255af6b39617a14fc57ced1205c0942 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 08:21:04 -0600 Subject: [PATCH 18/24] remove nullGroup --- .../alerts_table/grouping_settings/mock.ts | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts index 6416319a72a31..9e0b4e63715aa 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts @@ -50,7 +50,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -92,7 +91,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -134,7 +132,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -176,7 +173,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -218,7 +214,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -260,7 +255,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -302,7 +296,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -344,7 +337,6 @@ export const groupingSearchResponse = { unitsCount: { value: 300, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -386,7 +378,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -428,7 +419,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -470,7 +460,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -512,7 +501,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -554,7 +542,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -596,7 +583,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -638,7 +624,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -680,7 +665,6 @@ export const groupingSearchResponse = { unitsCount: { value: 273, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -722,7 +706,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -764,7 +747,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -806,7 +788,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -848,7 +829,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -890,7 +870,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -932,7 +911,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -974,7 +952,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1016,7 +993,6 @@ export const groupingSearchResponse = { unitsCount: { value: 100, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1058,7 +1034,6 @@ export const groupingSearchResponse = { unitsCount: { value: 91, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1110,7 +1085,6 @@ export const groupingSearchResponse = { unitsCount: { value: 75, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1137,7 +1111,6 @@ export const groupingSearchResponse = { unitsCount: { value: 63, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1164,7 +1137,6 @@ export const groupingSearchResponse = { unitsCount: { value: 51, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1191,7 +1163,6 @@ export const groupingSearchResponse = { unitsCount: { value: 42, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1218,7 +1189,6 @@ export const groupingSearchResponse = { unitsCount: { value: 33, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1245,7 +1215,6 @@ export const groupingSearchResponse = { unitsCount: { value: 30, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1272,7 +1241,6 @@ export const groupingSearchResponse = { unitsCount: { value: 27, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1299,7 +1267,6 @@ export const groupingSearchResponse = { unitsCount: { value: 27, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1326,7 +1293,6 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1353,7 +1319,6 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1380,7 +1345,6 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1407,7 +1371,6 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1434,7 +1397,6 @@ export const groupingSearchResponse = { unitsCount: { value: 24, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1461,7 +1423,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1488,7 +1449,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1515,7 +1475,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1542,7 +1501,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1569,7 +1527,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1596,7 +1553,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1623,7 +1579,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1650,7 +1605,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1677,7 +1631,6 @@ export const groupingSearchResponse = { unitsCount: { value: 21, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1704,7 +1657,6 @@ export const groupingSearchResponse = { unitsCount: { value: 18, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1731,7 +1683,6 @@ export const groupingSearchResponse = { unitsCount: { value: 18, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, @@ -1758,7 +1709,6 @@ export const groupingSearchResponse = { unitsCount: { value: 18, }, - nullGroup: { doc_count: 0 }, severitiesSubAggregation: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, From db83fe8c2605aacd865d0fb9374d432a985e9923 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 27 Apr 2023 14:51:18 +0000 Subject: [PATCH 19/24] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../src/containers/query/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts index 4ecf935b98760..5bccfb10f811a 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts @@ -8,7 +8,7 @@ import { ES_FIELD_TYPES } from '@kbn/field-types'; /** - * Returns a tuple of values according to the `esType` param, these values are meant to be applied in the _missing_ + * Returns a tuple of values according to the `esType` param, these values are meant to be applied in the _missing_ * property of the query aggregation of the grouping, to look up for missing values in the response buckets. * These values do not need to be anything in particular, the only requirement is they have to be 2 different values that validate against the field type. */ From dbb33cbf28b1a0cee97f3f991c13f5ce2793a6e3 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 09:08:09 -0600 Subject: [PATCH 20/24] fix mock --- .../alerts_table/alerts_grouping.test.tsx | 8 +- .../alerts_table/grouping_settings/mock.ts | 3050 +++++++---------- 2 files changed, 1331 insertions(+), 1727 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx index fb57db3555628..dae31cb56a79b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.test.tsx @@ -155,15 +155,9 @@ describe('GroupedAlertsTable', () => { if (i.skip) { return mockQueryResponse; } - if (i.query.aggs.groupByFields.multi_terms != null) { - return { - ...mockQueryResponse, - data: groupingSearchResponse.ruleName, - }; - } return { ...mockQueryResponse, - data: i.query.aggs.groupByFields.terms.field != null ? groupingSearchResponse.hostName : {}, + data: groupingSearchResponse, }; }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts index 9e0b4e63715aa..c9717c2a1ad2b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/mock.ts @@ -8,1729 +8,1339 @@ import { mockAlertSearchResponse } from '../../../../common/components/alerts_treemap/lib/mocks/mock_alert_search_response'; export const groupingSearchResponse = { - ruleName: { - ...mockAlertSearchResponse, - hits: { - total: { - value: 6048, - relation: 'eq', - }, - max_score: null, - hits: [], - }, - aggregations: { - groupsCount: { - value: 32, - }, - groupByFields: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: ['critical hosts [Duplicate]', 'f'], - key_as_string: 'critical hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts [Duplicate]', 'f'], - key_as_string: 'high hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'high hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts [Duplicate]', 'f'], - key_as_string: 'low hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'low hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts [Duplicate]', 'f'], - key_as_string: 'medium hosts [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts [Duplicate] [Duplicate]', 'f'], - key_as_string: 'medium hosts [Duplicate] [Duplicate]|f', - doc_count: 300, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 300, - }, - { - key: 'rule', - doc_count: 300, - }, - ], - }, - unitsCount: { - value: 300, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 300, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical users [Duplicate]', 'f'], - key_as_string: 'critical users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['critical users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['high users [Duplicate]', 'f'], - key_as_string: 'high users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['high users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'high users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['low users [Duplicate]', 'f'], - key_as_string: 'low users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['low users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'low users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['medium users [Duplicate]', 'f'], - key_as_string: 'medium users [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['medium users [Duplicate] [Duplicate]', 'f'], - key_as_string: 'medium users [Duplicate] [Duplicate]|f', - doc_count: 273, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 273, - }, - { - key: 'rule', - doc_count: 273, - }, - ], - }, - unitsCount: { - value: 273, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 273, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - { - key: ['critical hosts', 'f'], - key_as_string: 'critical hosts|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts', 'f'], - key_as_string: 'high hosts|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['high hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'high hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'high', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts ', 'f'], - key_as_string: 'low hosts |f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['low hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'low hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'low', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts', 'f'], - key_as_string: 'medium hosts|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['medium hosts [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'medium hosts [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 100, - hostsCountAggregation: { - value: 30, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 100, - }, - { - key: 'rule', - doc_count: 100, - }, - ], - }, - unitsCount: { - value: 100, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'medium', - doc_count: 100, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: ['critical users [Duplicate] [Duplicate] [Duplicate]', 'f'], - key_as_string: 'critical users [Duplicate] [Duplicate] [Duplicate]|f', - doc_count: 91, - hostsCountAggregation: { - value: 10, - }, - ruleTags: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'cool', - doc_count: 91, - }, - { - key: 'rule', - doc_count: 91, - }, - ], - }, - unitsCount: { - value: 91, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 91, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 91, - }, - }, - ], - }, - unitsCount: { - value: 6048, - }, + ...mockAlertSearchResponse, + hits: { + total: { + value: 6048, + relation: 'eq', }, + max_score: null, + hits: [], }, - hostName: { - ...mockAlertSearchResponse, - hits: { - total: { - value: 900, - relation: 'eq', - }, - max_score: null, - hits: [], + aggregations: { + groupsCount: { + value: 32, + }, + groupByFields: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: ['critical hosts [Duplicate]', 'critical hosts [Duplicate]'], + key_as_string: 'critical hosts [Duplicate]|critical hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical hosts [Duplicate] [Duplicate]', 'critical hosts [Duplicate] [Duplicate]'], + key_as_string: + 'critical hosts [Duplicate] [Duplicate]|critical hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts [Duplicate]', 'high hosts [Duplicate]'], + key_as_string: 'high hosts [Duplicate]|high hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts [Duplicate] [Duplicate]', 'high hosts [Duplicate] [Duplicate]'], + key_as_string: 'high hosts [Duplicate] [Duplicate]|high hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts [Duplicate]', 'low hosts [Duplicate]'], + key_as_string: 'low hosts [Duplicate]|low hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts [Duplicate] [Duplicate]', 'low hosts [Duplicate] [Duplicate]'], + key_as_string: 'low hosts [Duplicate] [Duplicate]|low hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts [Duplicate]', 'medium hosts [Duplicate]'], + key_as_string: 'medium hosts [Duplicate]|medium hosts [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts [Duplicate] [Duplicate]', 'medium hosts [Duplicate] [Duplicate]'], + key_as_string: + 'medium hosts [Duplicate] [Duplicate]|medium hosts [Duplicate] [Duplicate]', + doc_count: 300, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 300, + }, + { + key: 'rule', + doc_count: 300, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 300, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 300, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['critical users [Duplicate]', 'critical users [Duplicate]'], + key_as_string: 'critical users [Duplicate]|critical users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: [ + 'critical users [Duplicate] [Duplicate]', + 'critical users [Duplicate] [Duplicate]', + ], + key_as_string: + 'critical users [Duplicate] [Duplicate]|critical users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['high users [Duplicate]', 'high users [Duplicate]'], + key_as_string: 'high users [Duplicate]|high users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['high users [Duplicate] [Duplicate]', 'high users [Duplicate] [Duplicate]'], + key_as_string: 'high users [Duplicate] [Duplicate]|high users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['low users [Duplicate]', 'low users [Duplicate]'], + key_as_string: 'low users [Duplicate]|low users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['low users [Duplicate] [Duplicate]', 'low users [Duplicate] [Duplicate]'], + key_as_string: 'low users [Duplicate] [Duplicate]|low users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['medium users [Duplicate]', 'medium users [Duplicate]'], + key_as_string: 'medium users [Duplicate]|medium users [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['medium users [Duplicate] [Duplicate]', 'medium users [Duplicate] [Duplicate]'], + key_as_string: + 'medium users [Duplicate] [Duplicate]|medium users [Duplicate] [Duplicate]', + doc_count: 273, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 273, + }, + { + key: 'rule', + doc_count: 273, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 273, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 273, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + { + key: ['critical hosts', 'critical hosts'], + key_as_string: 'critical hosts|critical hosts', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'critical hosts [Duplicate] [Duplicate] [Duplicate]', + 'critical hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'critical hosts [Duplicate] [Duplicate] [Duplicate]|critical hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['high hosts', 'high hosts'], + key_as_string: 'high hosts|high hosts', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'high hosts [Duplicate] [Duplicate] [Duplicate]', + 'high hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'high hosts [Duplicate] [Duplicate] [Duplicate]|high hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'high', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['low hosts ', 'low hosts '], + key_as_string: 'low hosts |low hosts ', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'low hosts [Duplicate] [Duplicate] [Duplicate]', + 'low hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'low hosts [Duplicate] [Duplicate] [Duplicate]|low hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'low', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: ['medium hosts', 'medium hosts'], + key_as_string: 'medium hosts|medium hosts', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'medium hosts [Duplicate] [Duplicate] [Duplicate]', + 'medium hosts [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'medium hosts [Duplicate] [Duplicate] [Duplicate]|medium hosts [Duplicate] [Duplicate] [Duplicate]', + doc_count: 100, + hostsCountAggregation: { + value: 30, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 100, + }, + { + key: 'rule', + doc_count: 100, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 100, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'medium', + doc_count: 100, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 0, + }, + }, + { + key: [ + 'critical users [Duplicate] [Duplicate] [Duplicate]', + 'critical users [Duplicate] [Duplicate] [Duplicate]', + ], + key_as_string: + 'critical users [Duplicate] [Duplicate] [Duplicate]|critical users [Duplicate] [Duplicate] [Duplicate]', + doc_count: 91, + hostsCountAggregation: { + value: 10, + }, + ruleTags: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'cool', + doc_count: 91, + }, + { + key: 'rule', + doc_count: 91, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], + }, + unitsCount: { + value: 91, + }, + severitiesSubAggregation: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'critical', + doc_count: 91, + }, + ], + }, + countSeveritySubAggregation: { + value: 1, + }, + usersCountAggregation: { + value: 91, + }, + }, + ], + }, + description: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'f', + doc_count: 1, + }, + ], }, - aggregations: { - groupsCount: { - value: 40, - }, - groupByFields: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'Host-f0m6ngo8fo', - doc_count: 75, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 75, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 75, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 25, - }, - }, - { - key: 'Host-4aijlqggv8', - doc_count: 63, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 63, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 63, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 21, - }, - }, - { - key: 'Host-e50lhbdm91', - doc_count: 51, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 51, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 51, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 17, - }, - }, - { - key: 'sqp', - doc_count: 42, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 42, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 42, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'sUl', - doc_count: 33, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 33, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 33, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'vLJ', - doc_count: 30, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 30, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 30, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Host-n28uwmsqmd', - doc_count: 27, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 27, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 27, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 9, - }, - }, - { - key: 'JaE', - doc_count: 27, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 27, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 27, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'CUA', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'FWT', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'ZqT', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'mmn', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'xRS', - doc_count: 24, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 24, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 24, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'HiC', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Host-d7zbfvl3zz', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 7, - }, - }, - { - key: 'Nnc', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'OqH', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Vaw', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'XPg', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'qBS', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'rwt', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'xVJ', - doc_count: 21, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 21, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 21, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'Bxg', - doc_count: 18, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 18, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 18, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'efP', - doc_count: 18, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 18, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 18, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - { - key: 'qcb', - doc_count: 18, - rulesCountAggregation: { - value: 3, - }, - unitsCount: { - value: 18, - }, - severitiesSubAggregation: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'critical', - doc_count: 18, - }, - ], - }, - countSeveritySubAggregation: { - value: 1, - }, - usersCountAggregation: { - value: 0, - }, - }, - ], - }, - unitsCount: { - value: 900, - }, + unitsCount: { + value: 6048, }, }, }; From 75f203785efe4e55c0362ba90695614beb96d268 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 09:10:57 -0600 Subject: [PATCH 21/24] pr follow up --- .../src/containers/query/helpers.ts | 2 ++ .../src/containers/query/index.ts | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts index bd7df712bd9f1..b42cb5341dba9 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts @@ -29,3 +29,5 @@ export function getFieldTypeMissingValues(esType: string[]): [number, number] | return ['-', '--']; } } + +export const getEmptyValue = () => '—'; diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 63f9301b35185..caeaed7375943 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { getFieldTypeMissingValues } from './helpers'; +import { getEmptyValue, getFieldTypeMissingValues } from './helpers'; import { GroupingBucket } from '../..'; import { RawBucket } from '../../..'; import type { GroupingQueryArgs, GroupingQuery } from './types'; @@ -117,6 +117,7 @@ export const parseGroupingQuery = ( ): Array & GroupingBucket> => buckets.map((group) => { const groupKeyArray = Array.isArray(group.key) ? group.key : [group.key]; + const emptyValue = getEmptyValue(); return groupKeyArray[0] === groupKeyArray[1] ? { ...group, @@ -125,8 +126,8 @@ export const parseGroupingQuery = ( } : { ...group, - key: ['-'], - key_as_string: '-', + key: [emptyValue], + key_as_string: emptyValue, isNullGroup: true, }; }); From 12b54a4aa4da7621348c39840af36a1570558869 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 09:14:02 -0600 Subject: [PATCH 22/24] fix bad merge --- .../src/containers/query/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index 6d2c62f777d23..bd91c0a2cde47 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -122,11 +122,11 @@ export const parseGroupingQuery = ( const emptyValue = getEmptyValue(); // If the keys are different means that the `missing` values of the multi_terms aggregation have been applied, we use the default empty string. // If the keys are equal means the `missing` values have not been applied, they are stored values. - return groupKeyArray[0] === groupKeyArray[1] + return group.key[0] === group.key[1] ? { ...group, - key: [groupKeyArray[0]], - key_as_string: groupKeyArray[0], + key: [group.key[0]], + key_as_string: group.key[0], } : { ...group, From 0d345e0c10c57228b4c0ae418e9304fd712069ed Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 09:35:21 -0600 Subject: [PATCH 23/24] update link --- .../src/containers/query/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts index bd91c0a2cde47..e4440bc50e249 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.ts @@ -58,8 +58,8 @@ export const getGroupingQuery = ({ { field: groupByField, // the AggregationsMultiTermLookup type is wrong in the elasticsearch node package - // see linked slack thread to find out when we can remove these ts expect errors - // https://urle.me/eAq + // when this issues is resolved, we can remove these ts expect errors + // https://github.com/elastic/elasticsearch/issues/95628 // @ts-expect-error missing: getFieldTypeMissingValues(selectedGroupEsTypes)[0], }, From 33b4153ec64b00183aae01ec994c22d8b8dc4514 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 27 Apr 2023 13:07:21 -0600 Subject: [PATCH 24/24] quick test fix --- .../src/containers/query/index.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts index 2502dbf8854fc..c3ab014119db7 100644 --- a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts +++ b/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts @@ -8,6 +8,7 @@ import type { GroupingQueryArgs } from './types'; import { getGroupingQuery, parseGroupingQuery } from '.'; +import { getEmptyValue } from './helpers'; const testProps: GroupingQueryArgs = { additionalFilters: [], @@ -185,8 +186,8 @@ describe('group selector', () => { doc_count: 75, }, { - key: ['-'], - key_as_string: '-', + key: [getEmptyValue()], + key_as_string: getEmptyValue(), isNullGroup: true, doc_count: 75, },