mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution] Improve grouping interfaces naming (#153124)
Change/ cleaned up grouping package interfaces <img width="488" alt="Screenshot 2023-03-09 at 10 07 06 PM" src="https://user-images.githubusercontent.com/55110838/224393885-44e61967-8b45-4b8d-bb68-d5e152a3ca14.png"> --------- Co-authored-by: Steph Milovic <stephanie.milovic@elastic.co>
This commit is contained in:
parent
f4e6ef75be
commit
238ff29c8b
14 changed files with 122 additions and 189 deletions
|
@ -24,10 +24,10 @@ const rule2Desc = 'Rule 2 description';
|
|||
|
||||
const testProps = {
|
||||
data: {
|
||||
groupCount0: {
|
||||
groupsCount: {
|
||||
value: 2,
|
||||
},
|
||||
stackByMultipleFields0: {
|
||||
groupByFields: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
|
@ -75,7 +75,7 @@ const testProps = {
|
|||
sum_other_doc_count: 0,
|
||||
buckets: [],
|
||||
},
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
value: 1,
|
||||
},
|
||||
severitiesSubAggregation: {
|
||||
|
@ -97,7 +97,7 @@ const testProps = {
|
|||
},
|
||||
],
|
||||
},
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
value: 2,
|
||||
},
|
||||
},
|
||||
|
@ -120,7 +120,7 @@ describe('grouping container', () => {
|
|||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('Renders group counts when groupCount0 > 0', () => {
|
||||
it('Renders groups count when groupsCount > 0', () => {
|
||||
const { getByTestId, getAllByTestId, queryByTestId } = render(
|
||||
<I18nProvider>
|
||||
<Grouping {...testProps} />
|
||||
|
@ -132,17 +132,17 @@ describe('grouping container', () => {
|
|||
expect(queryByTestId('empty-results-panel')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not render group counts when groupCount0 = 0', () => {
|
||||
it('Does not render group counts when groupsCount = 0', () => {
|
||||
const data = {
|
||||
groupCount0: {
|
||||
groupsCount: {
|
||||
value: 0,
|
||||
},
|
||||
stackByMultipleFields0: {
|
||||
groupByFields: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [],
|
||||
},
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
value: 0,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -81,12 +81,12 @@ const GroupingComponent = <T,>({
|
|||
Record<string, { state: 'open' | 'closed' | undefined; selectedBucket: RawBucket<T> }>
|
||||
>({});
|
||||
|
||||
const unitCount = data?.unitCount0?.value ?? 0;
|
||||
const unitCount = data?.unitsCount?.value ?? 0;
|
||||
const unitCountText = useMemo(() => {
|
||||
return `${unitCount.toLocaleString()} ${unit && unit(unitCount)}`;
|
||||
}, [unitCount, unit]);
|
||||
|
||||
const groupCount = data?.groupCount0?.value ?? 0;
|
||||
const groupCount = data?.groupsCount?.value ?? 0;
|
||||
const groupCountText = useMemo(
|
||||
() => `${groupCount.toLocaleString()} ${GROUPS_UNIT(groupCount)}`,
|
||||
[groupCount]
|
||||
|
@ -94,7 +94,7 @@ const GroupingComponent = <T,>({
|
|||
|
||||
const groupPanels = useMemo(
|
||||
() =>
|
||||
data?.stackByMultipleFields0?.buckets?.map((groupBucket, groupNumber) => {
|
||||
data?.groupByFields?.buckets?.map((groupBucket, groupNumber) => {
|
||||
const group = firstNonNullValue(groupBucket.key);
|
||||
const groupKey = `group-${groupNumber}-${group}`;
|
||||
|
||||
|
@ -145,7 +145,7 @@ const GroupingComponent = <T,>({
|
|||
[
|
||||
badgeMetricStats,
|
||||
customMetricStats,
|
||||
data?.stackByMultipleFields0?.buckets,
|
||||
data?.groupByFields?.buckets,
|
||||
groupPanelRenderer,
|
||||
groupingId,
|
||||
isLoading,
|
||||
|
|
|
@ -20,13 +20,13 @@ export type RawBucket<T> = GenericBuckets & T;
|
|||
/** Defines the shape of the aggregation returned by Elasticsearch */
|
||||
// TODO: write developer docs for these fields
|
||||
export interface GroupingAggregation<T> {
|
||||
stackByMultipleFields0?: {
|
||||
groupByFields?: {
|
||||
buckets?: Array<RawBucket<T>>;
|
||||
};
|
||||
groupCount0?: {
|
||||
groupsCount?: {
|
||||
value?: number | null;
|
||||
};
|
||||
unitCount0?: {
|
||||
unitsCount?: {
|
||||
value?: number | null;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,8 +11,9 @@ import { getGroupingQuery, MAX_QUERY_SIZE } from '.';
|
|||
|
||||
const testProps: GroupingQueryArgs = {
|
||||
additionalFilters: [],
|
||||
additionalAggregationsRoot: [],
|
||||
additionalStatsAggregationsFields0: [
|
||||
from: '2022-12-28T15:35:32.871Z',
|
||||
groupByFields: ['host.name'],
|
||||
metricsAggregations: [
|
||||
{
|
||||
alertsCount: {
|
||||
cardinality: {
|
||||
|
@ -49,14 +50,10 @@ const testProps: GroupingQueryArgs = {
|
|||
},
|
||||
},
|
||||
],
|
||||
additionalStatsAggregationsFields1: [],
|
||||
from: '2022-12-28T15:35:32.871Z',
|
||||
pageNumber: 0,
|
||||
rootAggregations: [],
|
||||
runtimeMappings: {},
|
||||
stackByMultipleFields0: ['host.name'],
|
||||
stackByMultipleFields0Size: 25,
|
||||
stackByMultipleFields0From: 0,
|
||||
stackByMultipleFields1: [],
|
||||
stackByMultipleFields1Size: 10,
|
||||
size: 25,
|
||||
to: '2023-02-23T06:59:59.999Z',
|
||||
};
|
||||
describe('group selector', () => {
|
||||
|
@ -65,12 +62,12 @@ describe('group selector', () => {
|
|||
});
|
||||
it('Sets terms query when single stackBy field requested', () => {
|
||||
const result = getGroupingQuery(testProps);
|
||||
expect(result.aggs.stackByMultipleFields0.multi_terms).toBeUndefined();
|
||||
expect(result.aggs.stackByMultipleFields0.terms).toEqual({
|
||||
expect(result.aggs.groupByFields.multi_terms).toBeUndefined();
|
||||
expect(result.aggs.groupByFields.terms).toEqual({
|
||||
field: 'host.name',
|
||||
size: MAX_QUERY_SIZE,
|
||||
});
|
||||
expect(result.aggs.stackByMultipleFields0.aggs).toEqual({
|
||||
expect(result.aggs.groupByFields.aggs).toEqual({
|
||||
bucket_truncate: { bucket_sort: { from: 0, size: 25 } },
|
||||
alertsCount: { cardinality: { field: 'kibana.alert.uuid' } },
|
||||
rulesCountAggregation: { cardinality: { field: 'kibana.alert.rule.rule_id' } },
|
||||
|
@ -85,7 +82,7 @@ describe('group selector', () => {
|
|||
it('Sets terms query when single stackBy field requested additionalAggregationsRoot', () => {
|
||||
const result = getGroupingQuery({
|
||||
...testProps,
|
||||
additionalAggregationsRoot: [
|
||||
rootAggregations: [
|
||||
{
|
||||
alertsCount: {
|
||||
terms: {
|
||||
|
@ -111,10 +108,10 @@ describe('group selector', () => {
|
|||
it('Sets terms query when multiple stackBy fields requested', () => {
|
||||
const result = getGroupingQuery({
|
||||
...testProps,
|
||||
stackByMultipleFields0: ['kibana.alert.rule.name', 'kibana.alert.rule.description'],
|
||||
groupByFields: ['kibana.alert.rule.name', 'kibana.alert.rule.description'],
|
||||
});
|
||||
expect(result.aggs.stackByMultipleFields0.terms).toBeUndefined();
|
||||
expect(result.aggs.stackByMultipleFields0.multi_terms).toEqual({
|
||||
expect(result.aggs.groupByFields.terms).toBeUndefined();
|
||||
expect(result.aggs.groupByFields.multi_terms).toEqual({
|
||||
terms: [{ field: 'kibana.alert.rule.name' }, { field: 'kibana.alert.rule.description' }],
|
||||
size: MAX_QUERY_SIZE,
|
||||
});
|
||||
|
|
|
@ -6,111 +6,58 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import type { GroupingQueryArgs, GroupingQuery, NamedAggregation } from './types';
|
||||
/** The maximum number of items to render */
|
||||
export const DEFAULT_STACK_BY_FIELD0_SIZE = 10;
|
||||
export const DEFAULT_STACK_BY_FIELD1_SIZE = 10;
|
||||
|
||||
const getOptionalSubAggregation = ({
|
||||
stackByMultipleFields1,
|
||||
stackByMultipleFields1Size,
|
||||
stackByMultipleFields1From = 0,
|
||||
stackByMultipleFields1Sort,
|
||||
additionalStatsAggregationsFields1,
|
||||
}: {
|
||||
stackByMultipleFields1: string[] | undefined;
|
||||
stackByMultipleFields1Size: number;
|
||||
stackByMultipleFields1From?: number;
|
||||
stackByMultipleFields1Sort?: Array<{ [category: string]: { order: 'asc' | 'desc' } }>;
|
||||
additionalStatsAggregationsFields1: NamedAggregation[];
|
||||
}): NamedAggregation | {} =>
|
||||
stackByMultipleFields1 != null && !isEmpty(stackByMultipleFields1)
|
||||
? {
|
||||
stackByMultipleFields1: {
|
||||
multi_terms: {
|
||||
terms: stackByMultipleFields1.map((stackByMultipleField1) => ({
|
||||
field: stackByMultipleField1,
|
||||
})),
|
||||
},
|
||||
aggs: {
|
||||
bucket_truncate: {
|
||||
bucket_sort: {
|
||||
sort: stackByMultipleFields1Sort,
|
||||
from: stackByMultipleFields1From,
|
||||
size: stackByMultipleFields1Size,
|
||||
},
|
||||
},
|
||||
...additionalStatsAggregationsFields1.reduce(
|
||||
(aggObj, subAgg) => Object.assign(aggObj, subAgg),
|
||||
{}
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
: {};
|
||||
import type { GroupingQueryArgs, GroupingQuery } from './types';
|
||||
/** The maximum number of groups to render */
|
||||
export const DEFAULT_GROUP_BY_FIELD_SIZE = 10;
|
||||
|
||||
// our pagination will be broken if the stackBy field cardinality exceeds 10,000
|
||||
// https://github.com/elastic/kibana/issues/151913
|
||||
export const MAX_QUERY_SIZE = 10000;
|
||||
export const getGroupingQuery = ({
|
||||
additionalFilters = [],
|
||||
additionalAggregationsRoot,
|
||||
additionalStatsAggregationsFields0,
|
||||
additionalStatsAggregationsFields1,
|
||||
from,
|
||||
groupByFields,
|
||||
metricsAggregations,
|
||||
pageNumber,
|
||||
rootAggregations,
|
||||
runtimeMappings,
|
||||
stackByMultipleFields0,
|
||||
stackByMultipleFields0Size = DEFAULT_STACK_BY_FIELD0_SIZE,
|
||||
stackByMultipleFields0From,
|
||||
stackByMultipleFields0Sort,
|
||||
stackByMultipleFields1,
|
||||
stackByMultipleFields1Size = DEFAULT_STACK_BY_FIELD1_SIZE,
|
||||
stackByMultipleFields1From,
|
||||
stackByMultipleFields1Sort,
|
||||
size = DEFAULT_GROUP_BY_FIELD_SIZE,
|
||||
sort,
|
||||
to,
|
||||
}: GroupingQueryArgs): GroupingQuery => ({
|
||||
size: 0,
|
||||
aggs: {
|
||||
stackByMultipleFields0: {
|
||||
...(stackByMultipleFields0.length > 1
|
||||
groupByFields: {
|
||||
...(groupByFields.length > 1
|
||||
? {
|
||||
multi_terms: {
|
||||
terms: stackByMultipleFields0.map((stackByMultipleField0) => ({
|
||||
field: stackByMultipleField0,
|
||||
terms: groupByFields.map((groupByField) => ({
|
||||
field: groupByField,
|
||||
})),
|
||||
size: MAX_QUERY_SIZE,
|
||||
},
|
||||
}
|
||||
: {
|
||||
terms: {
|
||||
field: stackByMultipleFields0[0],
|
||||
field: groupByFields[0],
|
||||
size: MAX_QUERY_SIZE,
|
||||
},
|
||||
}),
|
||||
aggs: {
|
||||
...getOptionalSubAggregation({
|
||||
stackByMultipleFields1,
|
||||
stackByMultipleFields1Size,
|
||||
stackByMultipleFields1From,
|
||||
stackByMultipleFields1Sort,
|
||||
additionalStatsAggregationsFields1,
|
||||
}),
|
||||
bucket_truncate: {
|
||||
bucket_sort: {
|
||||
sort: stackByMultipleFields0Sort,
|
||||
from: stackByMultipleFields0From,
|
||||
size: stackByMultipleFields0Size,
|
||||
sort,
|
||||
from: pageNumber,
|
||||
size,
|
||||
},
|
||||
},
|
||||
...additionalStatsAggregationsFields0.reduce(
|
||||
(aggObj, subAgg) => Object.assign(aggObj, subAgg),
|
||||
{}
|
||||
),
|
||||
...(metricsAggregations
|
||||
? metricsAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {})
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
...(additionalAggregationsRoot
|
||||
? additionalAggregationsRoot.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {})
|
||||
...(rootAggregations
|
||||
? rootAggregations.reduce((aggObj, subAgg) => Object.assign(aggObj, subAgg), {})
|
||||
: {}),
|
||||
},
|
||||
query: {
|
||||
|
|
|
@ -23,37 +23,32 @@ export type NamedAggregation = Record<string, estypes.AggregationsAggregationCon
|
|||
export interface GroupingQueryArgs {
|
||||
additionalFilters: BoolAgg[];
|
||||
from: string;
|
||||
groupByFields: string[];
|
||||
metricsAggregations?: NamedAggregation[];
|
||||
pageNumber?: number;
|
||||
rootAggregations?: NamedAggregation[];
|
||||
runtimeMappings?: MappingRuntimeFields;
|
||||
additionalAggregationsRoot?: NamedAggregation[];
|
||||
stackByMultipleFields0: string[];
|
||||
stackByMultipleFields0Size?: number;
|
||||
stackByMultipleFields0From?: number;
|
||||
stackByMultipleFields0Sort?: Array<{ [category: string]: { order: 'asc' | 'desc' } }>;
|
||||
additionalStatsAggregationsFields0: NamedAggregation[];
|
||||
stackByMultipleFields1: string[] | undefined;
|
||||
stackByMultipleFields1Size?: number;
|
||||
stackByMultipleFields1From?: number;
|
||||
stackByMultipleFields1Sort?: Array<{ [category: string]: { order: estypes.SortOrder } }>;
|
||||
additionalStatsAggregationsFields1: NamedAggregation[];
|
||||
size?: number;
|
||||
sort?: Array<{ [category: string]: { order: 'asc' | 'desc' } }>;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export interface MainAggregation extends NamedAggregation {
|
||||
stackByMultipleFields0: {
|
||||
terms?: estypes.AggregationsAggregationContainer['terms'];
|
||||
multi_terms?: estypes.AggregationsAggregationContainer['multi_terms'];
|
||||
groupByFields: {
|
||||
aggs: NamedAggregation;
|
||||
multi_terms?: estypes.AggregationsAggregationContainer['multi_terms'];
|
||||
terms?: estypes.AggregationsAggregationContainer['terms'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface GroupingQuery extends estypes.QueryDslQueryContainer {
|
||||
size: number;
|
||||
runtime_mappings: MappingRuntimeFields | undefined;
|
||||
aggs: MainAggregation;
|
||||
query: {
|
||||
bool: {
|
||||
filter: Array<BoolAgg | RangeAgg>;
|
||||
};
|
||||
};
|
||||
runtime_mappings: MappingRuntimeFields | undefined;
|
||||
size: number;
|
||||
_source: boolean;
|
||||
aggs: MainAggregation;
|
||||
}
|
||||
|
|
|
@ -39,9 +39,9 @@ import { ALERTS_QUERY_NAMES } from '../../containers/detection_engine/alerts/con
|
|||
import {
|
||||
getAlertsGroupingQuery,
|
||||
getDefaultGroupingOptions,
|
||||
getSelectedGroupBadgeMetrics,
|
||||
getSelectedGroupButtonContent,
|
||||
getSelectedGroupCustomMetrics,
|
||||
getBadgeMetrics,
|
||||
renderGroupPanel,
|
||||
getCustomMetrics,
|
||||
useGroupTakeActionsItems,
|
||||
} from './grouping_settings';
|
||||
import { updateGroupSelector, updateSelectedGroup } from '../../../common/store/grouping/actions';
|
||||
|
@ -250,13 +250,13 @@ export const GroupedAlertsTableComponent: React.FC<AlertsTableComponentProps> =
|
|||
? renderChildComponent([])
|
||||
: getGrouping({
|
||||
badgeMetricStats: (fieldBucket: RawBucket<AlertsGroupingAggregation>) =>
|
||||
getSelectedGroupBadgeMetrics(selectedGroup, fieldBucket),
|
||||
getBadgeMetrics(selectedGroup, fieldBucket),
|
||||
customMetricStats: (fieldBucket: RawBucket<AlertsGroupingAggregation>) =>
|
||||
getSelectedGroupCustomMetrics(selectedGroup, fieldBucket),
|
||||
getCustomMetrics(selectedGroup, fieldBucket),
|
||||
data: alertsGroupsData?.aggregations,
|
||||
groupingId: tableId,
|
||||
groupPanelRenderer: (fieldBucket: RawBucket<AlertsGroupingAggregation>) =>
|
||||
getSelectedGroupButtonContent(selectedGroup, fieldBucket),
|
||||
renderGroupPanel(selectedGroup, fieldBucket),
|
||||
inspectButton: inspect,
|
||||
isLoading: loading || isLoadingGroups,
|
||||
onToggleCallback: (param) => {
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { getSelectedGroupButtonContent } from '.';
|
||||
import { renderGroupPanel } from '.';
|
||||
|
||||
describe('getSelectedGroupButtonContent', () => {
|
||||
describe('renderGroupPanel', () => {
|
||||
it('renders correctly when the field renderer exists', () => {
|
||||
const wrapperRuleName = shallow(
|
||||
getSelectedGroupButtonContent('kibana.alert.rule.name', {
|
||||
renderGroupPanel('kibana.alert.rule.name', {
|
||||
key: ['Rule name test', 'Some description'],
|
||||
doc_count: 10,
|
||||
})!
|
||||
|
@ -20,7 +20,7 @@ describe('getSelectedGroupButtonContent', () => {
|
|||
|
||||
expect(wrapperRuleName.find('[data-test-subj="rule-name-group-renderer"]')).toBeTruthy();
|
||||
const wrapperHostName = shallow(
|
||||
getSelectedGroupButtonContent('host.name', {
|
||||
renderGroupPanel('host.name', {
|
||||
key: 'Host',
|
||||
doc_count: 2,
|
||||
})!
|
||||
|
@ -28,7 +28,7 @@ describe('getSelectedGroupButtonContent', () => {
|
|||
|
||||
expect(wrapperHostName.find('[data-test-subj="host-name-group-renderer"]')).toBeTruthy();
|
||||
const wrapperUserName = shallow(
|
||||
getSelectedGroupButtonContent('user.name', {
|
||||
renderGroupPanel('user.name', {
|
||||
key: 'User test',
|
||||
doc_count: 1,
|
||||
})!
|
||||
|
@ -36,7 +36,7 @@ describe('getSelectedGroupButtonContent', () => {
|
|||
|
||||
expect(wrapperUserName.find('[data-test-subj="host-name-group-renderer"]')).toBeTruthy();
|
||||
const wrapperSourceIp = shallow(
|
||||
getSelectedGroupButtonContent('source.ip', {
|
||||
renderGroupPanel('source.ip', {
|
||||
key: 'sourceIp',
|
||||
doc_count: 23,
|
||||
})!
|
||||
|
@ -46,7 +46,7 @@ describe('getSelectedGroupButtonContent', () => {
|
|||
});
|
||||
|
||||
it('returns undefined when the renderer does not exist', () => {
|
||||
const wrapper = getSelectedGroupButtonContent('process.name', {
|
||||
const wrapper = renderGroupPanel('process.name', {
|
||||
key: 'process',
|
||||
doc_count: 10,
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ 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 getSelectedGroupButtonContent = (
|
||||
export const renderGroupPanel = (
|
||||
selectedGroup: string,
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
) => {
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getSelectedGroupBadgeMetrics } from '.';
|
||||
import { getBadgeMetrics } from '.';
|
||||
|
||||
describe('getSelectedGroupBadgeMetrics', () => {
|
||||
describe('getBadgeMetrics', () => {
|
||||
it('returns array of badges which roccespondes to the field name', () => {
|
||||
const badgesRuleName = getSelectedGroupBadgeMetrics('kibana.alert.rule.name', {
|
||||
const badgesRuleName = getBadgeMetrics('kibana.alert.rule.name', {
|
||||
key: ['Rule name test', 'Some description'],
|
||||
usersCountAggregation: {
|
||||
value: 10,
|
||||
|
@ -24,7 +24,7 @@ describe('getSelectedGroupBadgeMetrics', () => {
|
|||
badgesRuleName.find((badge) => badge.title === 'Alerts:' && badge.value === 10)
|
||||
).toBeTruthy();
|
||||
|
||||
const badgesHostName = getSelectedGroupBadgeMetrics('host.name', {
|
||||
const badgesHostName = getBadgeMetrics('host.name', {
|
||||
key: 'Host',
|
||||
rulesCountAggregation: {
|
||||
value: 3,
|
||||
|
@ -36,7 +36,7 @@ describe('getSelectedGroupBadgeMetrics', () => {
|
|||
badgesHostName.find((badge) => badge.title === 'Rules:' && badge.value === 3)
|
||||
).toBeTruthy();
|
||||
|
||||
const badgesUserName = getSelectedGroupBadgeMetrics('user.name', {
|
||||
const badgesUserName = getBadgeMetrics('user.name', {
|
||||
key: 'User test',
|
||||
hostsCountAggregation: {
|
||||
value: 1,
|
||||
|
@ -49,7 +49,7 @@ describe('getSelectedGroupBadgeMetrics', () => {
|
|||
});
|
||||
|
||||
it('returns default badges if the field specific does not exist', () => {
|
||||
const badges = getSelectedGroupBadgeMetrics('process.name', {
|
||||
const badges = getBadgeMetrics('process.name', {
|
||||
key: 'process',
|
||||
rulesCountAggregation: {
|
||||
value: 3,
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { RawBucket } from '@kbn/securitysolution-grouping';
|
|||
import type { AlertsGroupingAggregation } from './types';
|
||||
import * as i18n from '../translations';
|
||||
|
||||
const getSingleGroupSeverity = (severity?: string) => {
|
||||
const getSeverity = (severity?: string) => {
|
||||
switch (severity) {
|
||||
case 'low':
|
||||
return (
|
||||
|
@ -64,7 +64,7 @@ const multiSeverity = (
|
|||
</>
|
||||
);
|
||||
|
||||
export const getSelectedGroupBadgeMetrics = (
|
||||
export const getBadgeMetrics = (
|
||||
selectedGroup: string,
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
) => {
|
||||
|
@ -135,13 +135,13 @@ export const getSelectedGroupBadgeMetrics = (
|
|||
];
|
||||
};
|
||||
|
||||
export const getSelectedGroupCustomMetrics = (
|
||||
export const getCustomMetrics = (
|
||||
selectedGroup: string,
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
) => {
|
||||
const singleSeverityComponent =
|
||||
bucket.severitiesSubAggregation?.buckets && bucket.severitiesSubAggregation?.buckets?.length
|
||||
? getSingleGroupSeverity(bucket.severitiesSubAggregation?.buckets[0].key.toString())
|
||||
? getSeverity(bucket.severitiesSubAggregation?.buckets[0].key.toString())
|
||||
: null;
|
||||
const severityComponent =
|
||||
bucket.countSeveritySubAggregation?.value && bucket.countSeveritySubAggregation?.value > 1
|
||||
|
|
|
@ -43,19 +43,19 @@ describe('getAlertsGroupingQuery', () => {
|
|||
expect(groupingQuery).toStrictEqual({
|
||||
_source: false,
|
||||
aggs: {
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
value_count: {
|
||||
field: 'kibana.alert.rule.name',
|
||||
},
|
||||
},
|
||||
groupCount0: {
|
||||
groupsCount: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.rule.name',
|
||||
},
|
||||
},
|
||||
stackByMultipleFields0: {
|
||||
groupByFields: {
|
||||
aggs: {
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.uuid',
|
||||
},
|
||||
|
@ -180,19 +180,19 @@ describe('getAlertsGroupingQuery', () => {
|
|||
expect(groupingQuery).toStrictEqual({
|
||||
_source: false,
|
||||
aggs: {
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
value_count: {
|
||||
field: 'process.name',
|
||||
},
|
||||
},
|
||||
groupCount0: {
|
||||
groupsCount: {
|
||||
cardinality: {
|
||||
field: 'process.name',
|
||||
},
|
||||
},
|
||||
stackByMultipleFields0: {
|
||||
groupByFields: {
|
||||
aggs: {
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.uuid',
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
|
||||
import type { BoolQuery } from '@kbn/es-query';
|
||||
import type { NamedAggregation } from '@kbn/securitysolution-grouping';
|
||||
import { getGroupingQuery } from '@kbn/securitysolution-grouping';
|
||||
import { isNoneGroup, getGroupingQuery } from '@kbn/securitysolution-grouping';
|
||||
|
||||
const getGroupFields = (groupValue: string) => {
|
||||
if (groupValue === 'kibana.alert.rule.name') {
|
||||
|
@ -19,60 +19,51 @@ const getGroupFields = (groupValue: string) => {
|
|||
};
|
||||
|
||||
interface AlertsGroupingQueryParams {
|
||||
from: string;
|
||||
to: string;
|
||||
additionalFilters: Array<{
|
||||
bool: BoolQuery;
|
||||
}>;
|
||||
selectedGroup: string;
|
||||
runtimeMappings: MappingRuntimeFields;
|
||||
pageSize: number;
|
||||
from: string;
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
runtimeMappings: MappingRuntimeFields;
|
||||
selectedGroup: string;
|
||||
to: string;
|
||||
}
|
||||
|
||||
export const getAlertsGroupingQuery = ({
|
||||
from,
|
||||
to,
|
||||
additionalFilters,
|
||||
selectedGroup,
|
||||
runtimeMappings,
|
||||
pageSize,
|
||||
from,
|
||||
pageIndex,
|
||||
pageSize,
|
||||
runtimeMappings,
|
||||
selectedGroup,
|
||||
to,
|
||||
}: AlertsGroupingQueryParams) =>
|
||||
getGroupingQuery({
|
||||
additionalFilters,
|
||||
additionalAggregationsRoot: [
|
||||
from,
|
||||
groupByFields: !isNoneGroup(selectedGroup) ? getGroupFields(selectedGroup) : [],
|
||||
metricsAggregations: !isNoneGroup(selectedGroup)
|
||||
? getAggregationsByGroupField(selectedGroup)
|
||||
: [],
|
||||
pageNumber: pageIndex * pageSize,
|
||||
rootAggregations: [
|
||||
{
|
||||
unitCount0: { value_count: { field: selectedGroup } },
|
||||
unitsCount: { value_count: { field: selectedGroup } },
|
||||
},
|
||||
...(selectedGroup !== 'none'
|
||||
? [
|
||||
{
|
||||
groupCount0: {
|
||||
cardinality: {
|
||||
field: selectedGroup,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
...(!isNoneGroup(selectedGroup)
|
||||
? [{ groupsCount: { cardinality: { field: selectedGroup } } }]
|
||||
: []),
|
||||
],
|
||||
from,
|
||||
runtimeMappings,
|
||||
stackByMultipleFields0: selectedGroup !== 'none' ? getGroupFields(selectedGroup) : [],
|
||||
size: pageSize,
|
||||
to,
|
||||
additionalStatsAggregationsFields0:
|
||||
selectedGroup !== 'none' ? getAggregationsByGroupField(selectedGroup) : [],
|
||||
stackByMultipleFields0Size: pageSize,
|
||||
stackByMultipleFields0From: pageIndex * pageSize,
|
||||
additionalStatsAggregationsFields1: [],
|
||||
stackByMultipleFields1: [],
|
||||
});
|
||||
|
||||
const getAggregationsByGroupField = (field: string): NamedAggregation[] => {
|
||||
const aggMetrics: NamedAggregation[] = [
|
||||
{
|
||||
unitCount0: {
|
||||
unitsCount: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.uuid',
|
||||
},
|
||||
|
|
|
@ -9,7 +9,7 @@ import type { GenericBuckets } from '@kbn/securitysolution-grouping/src';
|
|||
// Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
type NumberOrNull = number | null;
|
||||
export interface AlertsGroupingAggregation {
|
||||
unitCount0?: {
|
||||
unitsCount?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
severitiesSubAggregation?: {
|
||||
|
@ -24,6 +24,9 @@ export interface AlertsGroupingAggregation {
|
|||
hostsCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
ipsCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
rulesCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue