mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Fix topN grouping when field type is boolean (#131958)
* fix grouping * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * fix grouping * unit tests * fix types * update comment Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
693c0c3445
commit
3f5197a598
9 changed files with 186 additions and 17 deletions
|
@ -19,7 +19,7 @@ export interface EventSource {
|
|||
}
|
||||
|
||||
export interface EventsActionGroupData {
|
||||
key: number;
|
||||
key: number | string;
|
||||
events: {
|
||||
bucket: EventsMatrixHistogramData[];
|
||||
};
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { showInitialLoadingSpinner } from './helpers';
|
||||
import { formatAlertsData, showInitialLoadingSpinner } from './helpers';
|
||||
import { result, textResult, stackedByBooleanField, stackedByTextField } from './mock_data';
|
||||
|
||||
describe('helpers', () => {
|
||||
describe('showInitialLoadingSpinner', () => {
|
||||
|
@ -34,3 +35,15 @@ describe('helpers', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatAlertsData', () => {
|
||||
test('stack by a boolean field', () => {
|
||||
const res = formatAlertsData(stackedByBooleanField);
|
||||
expect(res).toEqual(result);
|
||||
});
|
||||
|
||||
test('stack by a text field', () => {
|
||||
const res = formatAlertsData(stackedByTextField);
|
||||
expect(res).toEqual(textResult);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,19 +17,22 @@ const EMPTY_ALERTS_DATA: HistogramData[] = [];
|
|||
export const formatAlertsData = (alertsData: AlertSearchResponse<{}, AlertsAggregation> | null) => {
|
||||
const groupBuckets: AlertsGroupBucket[] =
|
||||
alertsData?.aggregations?.alertsByGrouping?.buckets ?? [];
|
||||
return groupBuckets.reduce<HistogramData[]>((acc, { key: group, alerts }) => {
|
||||
const alertsBucket: AlertsBucket[] = alerts.buckets ?? [];
|
||||
return groupBuckets.reduce<HistogramData[]>(
|
||||
(acc, { key_as_string: keyAsString, key: group, alerts }) => {
|
||||
const alertsBucket: AlertsBucket[] = alerts.buckets ?? [];
|
||||
|
||||
return [
|
||||
...acc,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
...alertsBucket.map(({ key, doc_count }: AlertsBucket) => ({
|
||||
x: key,
|
||||
y: doc_count,
|
||||
g: group,
|
||||
})),
|
||||
];
|
||||
}, EMPTY_ALERTS_DATA);
|
||||
return [
|
||||
...acc,
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
...alertsBucket.map(({ key, doc_count }: AlertsBucket) => ({
|
||||
x: key,
|
||||
y: doc_count,
|
||||
g: keyAsString ?? group.toString(),
|
||||
})),
|
||||
];
|
||||
},
|
||||
EMPTY_ALERTS_DATA
|
||||
);
|
||||
};
|
||||
|
||||
export const getAlertsHistogramQuery = (
|
||||
|
|
|
@ -186,7 +186,7 @@ export const AlertsHistogramPanel = memo<AlertsHistogramPanelProps>(
|
|||
),
|
||||
field: selectedStackByOption,
|
||||
timelineId,
|
||||
value: bucket.key,
|
||||
value: bucket?.key_as_string ?? bucket.key,
|
||||
}))
|
||||
: NO_LEGEND_DATA,
|
||||
[
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
export const stackedByBooleanField = {
|
||||
took: 1,
|
||||
timed_out: false,
|
||||
_shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
|
||||
hits: {
|
||||
total: {
|
||||
value: 3,
|
||||
relation: 'eq',
|
||||
},
|
||||
hits: [],
|
||||
},
|
||||
timeout: false,
|
||||
aggregations: {
|
||||
alertsByGrouping: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{
|
||||
key: 1,
|
||||
key_as_string: 'true',
|
||||
doc_count: 2683,
|
||||
alerts: {
|
||||
buckets: [
|
||||
{ key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 },
|
||||
{ key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 0 },
|
||||
{ key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 0 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const result = [
|
||||
{ x: 1652196888075, y: 0, g: 'true' },
|
||||
{ x: 1652199588074, y: 0, g: 'true' },
|
||||
{ x: 1652202288073, y: 0, g: 'true' },
|
||||
];
|
||||
|
||||
export const stackedByTextField = {
|
||||
took: 1,
|
||||
timeout: false,
|
||||
_shards: { total: 1, successful: 1, skipped: 0, failed: 0 },
|
||||
hits: {
|
||||
total: {
|
||||
value: 3,
|
||||
relation: 'eq',
|
||||
},
|
||||
hits: [],
|
||||
},
|
||||
aggregations: {
|
||||
alertsByGrouping: {
|
||||
doc_count_error_upper_bound: 0,
|
||||
sum_other_doc_count: 0,
|
||||
buckets: [
|
||||
{
|
||||
key: 'MacBook-Pro.local',
|
||||
doc_count: 2706,
|
||||
alerts: {
|
||||
buckets: [
|
||||
{ key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 },
|
||||
{ key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 0 },
|
||||
{ key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 0 },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const textResult = [
|
||||
{ x: 1652196888075, y: 0, g: 'MacBook-Pro.local' },
|
||||
{ x: 1652199588074, y: 0, g: 'MacBook-Pro.local' },
|
||||
{ x: 1652202288073, y: 0, g: 'MacBook-Pro.local' },
|
||||
];
|
|
@ -26,7 +26,8 @@ export interface AlertsBucket {
|
|||
}
|
||||
|
||||
export interface AlertsGroupBucket {
|
||||
key: string;
|
||||
key: string | number;
|
||||
key_as_string?: string;
|
||||
alerts: {
|
||||
buckets: AlertsBucket[];
|
||||
};
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MatrixHistogramType } from '../../../../../common/search_strategy';
|
||||
import { getGenericData } from './helpers';
|
||||
import { stackedByBooleanField, stackedByTextField, result, textResult } from './mock_data';
|
||||
|
||||
describe('getGenericData', () => {
|
||||
test('stack by a boolean field', () => {
|
||||
const res = getGenericData<MatrixHistogramType.events>(stackedByBooleanField, 'events.bucket');
|
||||
expect(res).toEqual(result);
|
||||
});
|
||||
|
||||
test('stack by a text field', () => {
|
||||
const res = getGenericData<MatrixHistogramType.events>(stackedByTextField, 'events.bucket');
|
||||
expect(res).toEqual(textResult);
|
||||
});
|
||||
});
|
|
@ -18,7 +18,8 @@ export const getGenericData = <T>(
|
|||
): MatrixHistogramData[] => {
|
||||
let result: MatrixHistogramData[] = [];
|
||||
data.forEach((bucketData: unknown) => {
|
||||
const group = get('key', bucketData);
|
||||
// if key_as_string is present use it, else default to the existing key
|
||||
const group = get('key_as_string', bucketData) ?? get('key', bucketData);
|
||||
const histData = getOr([], keyBucket, bucketData).map(
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
({ key, doc_count }: MatrixHistogramBucket) => ({
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const stackedByBooleanField = [
|
||||
{
|
||||
key: 1,
|
||||
key_as_string: 'true',
|
||||
doc_count: 7125,
|
||||
events: {
|
||||
bucket: [
|
||||
{ key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 },
|
||||
{ key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 774 },
|
||||
{ key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 415 },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
export const result = [
|
||||
{ x: 1652196888075, y: 0, g: 'true' },
|
||||
{ x: 1652199588074, y: 774, g: 'true' },
|
||||
{ x: 1652202288073, y: 415, g: 'true' },
|
||||
];
|
||||
|
||||
export const stackedByTextField = [
|
||||
{
|
||||
key: 'MacBook-Pro.local',
|
||||
doc_count: 7103,
|
||||
events: {
|
||||
bucket: [
|
||||
{ key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 },
|
||||
{ key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 774 },
|
||||
{ key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 415 },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const textResult = [
|
||||
{ x: 1652196888075, y: 0, g: 'MacBook-Pro.local' },
|
||||
{ x: 1652199588074, y: 774, g: 'MacBook-Pro.local' },
|
||||
{ x: 1652202288073, y: 415, g: 'MacBook-Pro.local' },
|
||||
];
|
Loading…
Add table
Add a link
Reference in a new issue