Skip to content

Commit 3e0198d

Browse files
authored
[Security solution] Value report data view filtering fix (#241682)
1 parent fe656a3 commit 3e0198d

23 files changed

+570
-30
lines changed

x-pack/solutions/security/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/ai/alert_filtering_metric.test.ts

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import type { EuiThemeComputed } from '@elastic/eui';
99
import { getAlertFilteringMetricLensAttributes } from './alert_filtering_metric';
1010
import type { ExtraOptions } from '../../types';
11+
import { getAlertIndexFilter } from './helpers';
1112

1213
interface FormulaColumn {
1314
params: {
@@ -32,10 +33,12 @@ interface MathColumn {
3233
describe('getAlertFilteringMetricLensAttributes', () => {
3334
const defaultEuiTheme = {} as EuiThemeComputed;
3435
const defaultTotalAlerts = 1000;
36+
const defaultSignalIndexName = '.alerts-security.alerts-default';
3537

3638
const defaultArgs = {
3739
euiTheme: defaultEuiTheme,
3840
totalAlerts: defaultTotalAlerts,
41+
signalIndexName: defaultSignalIndexName,
3942
};
4043

4144
it('returns lens attributes with correct basic structure, datasource, column, and visualization configurations', () => {
@@ -107,19 +110,21 @@ describe('getAlertFilteringMetricLensAttributes', () => {
107110
type: 'index-pattern',
108111
},
109112
]);
110-
const mockFilters = [
113+
const mockFilters: ExtraOptions['filters'] = [
111114
{ meta: { alias: 'test filter', disabled: false, negate: false } },
112-
] as ExtraOptions['filters'];
113-
expect(getAlertFilteringMetricLensAttributes(defaultArgs).state.filters).toEqual([]);
115+
];
116+
expect(getAlertFilteringMetricLensAttributes(defaultArgs).state.filters).toEqual([
117+
getAlertIndexFilter(defaultSignalIndexName),
118+
]);
114119
expect(
115120
getAlertFilteringMetricLensAttributes({ ...defaultArgs, extraOptions: {} }).state.filters
116-
).toEqual([]);
121+
).toEqual([getAlertIndexFilter(defaultSignalIndexName)]);
117122
expect(
118123
getAlertFilteringMetricLensAttributes({
119124
...defaultArgs,
120125
extraOptions: { filters: mockFilters },
121126
}).state.filters
122-
).toEqual(mockFilters);
127+
).toEqual([getAlertIndexFilter(defaultSignalIndexName), ...mockFilters]);
123128
const testCases = [500, 0, 1000000];
124129
testCases.forEach((totalAlerts) => {
125130
const testResult = getAlertFilteringMetricLensAttributes({ ...defaultArgs, totalAlerts });
@@ -147,6 +152,43 @@ describe('getAlertFilteringMetricLensAttributes', () => {
147152
};
148153
expect(
149154
getAlertFilteringMetricLensAttributes({ ...defaultArgs, extraOptions }).state.filters
150-
).toEqual(extraOptions.filters);
155+
).toEqual([getAlertIndexFilter(defaultSignalIndexName), ...(extraOptions.filters ?? [])]);
156+
});
157+
158+
it('includes alert index filter in filters array', () => {
159+
const result = getAlertFilteringMetricLensAttributes(defaultArgs);
160+
const expectedFilter = getAlertIndexFilter(defaultSignalIndexName);
161+
expect(result.state.filters).toContainEqual(expectedFilter);
162+
});
163+
164+
it('handles different signal index names correctly', () => {
165+
const testCases = [
166+
'.alerts-security.alerts-default',
167+
'.alerts-security.alerts-custom-space',
168+
'custom-alerts-index',
169+
];
170+
171+
testCases.forEach((signalIndexName) => {
172+
const result = getAlertFilteringMetricLensAttributes({
173+
...defaultArgs,
174+
signalIndexName,
175+
});
176+
const expectedFilter = getAlertIndexFilter(signalIndexName);
177+
expect(result.state.filters).toContainEqual(expectedFilter);
178+
});
179+
});
180+
181+
it('combines alert index filter with extra options filters', () => {
182+
const mockFilters: ExtraOptions['filters'] = [
183+
{ meta: { alias: 'extra filter', disabled: false, negate: false } },
184+
];
185+
const result = getAlertFilteringMetricLensAttributes({
186+
...defaultArgs,
187+
extraOptions: { filters: mockFilters },
188+
});
189+
190+
expect(result.state.filters).toHaveLength(2);
191+
expect(result.state.filters[0]).toEqual(getAlertIndexFilter(defaultSignalIndexName));
192+
expect(result.state.filters[1]).toEqual(mockFilters[0]);
151193
});
152194
});

x-pack/solutions/security/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/ai/alert_filtering_metric.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,21 @@
66
*/
77

88
import type { EuiThemeComputed } from '@elastic/eui';
9+
import { getAlertIndexFilter } from './helpers';
910
import type { ExtraOptions, LensAttributes } from '../../types';
1011

1112
export type MyGetLensAttributes = (params: {
1213
stackByField?: string;
1314
euiTheme: EuiThemeComputed;
1415
extraOptions?: ExtraOptions;
1516
esql?: string;
17+
signalIndexName: string;
1618
totalAlerts: number;
1719
}) => LensAttributes;
1820

1921
export const getAlertFilteringMetricLensAttributes: MyGetLensAttributes = ({
2022
extraOptions,
23+
signalIndexName,
2124
totalAlerts,
2225
}) => {
2326
return {
@@ -75,7 +78,7 @@ export const getAlertFilteringMetricLensAttributes: MyGetLensAttributes = ({
7578
},
7679
},
7780
},
78-
filters: extraOptions?.filters ?? [],
81+
filters: [getAlertIndexFilter(signalIndexName), ...(extraOptions?.filters ?? [])],
7982
internalReferences: [],
8083
query: { language: 'kuery', query: '_id :*' },
8184
visualization: {

x-pack/solutions/security/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/ai/cost_savings_metric.test.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77

88
import { getCostSavingsMetricLensAttributes } from './cost_savings_metric';
99
import type { EuiThemeComputed } from '@elastic/eui';
10+
import { getAlertIndexFilter } from './helpers';
1011

1112
describe('getCostSavingsMetricLensAttributes', () => {
1213
const mockTheme = {} as EuiThemeComputed;
14+
const defaultSignalIndexName = '.alerts-security.alerts-default';
1315
const defaultParams = {
1416
euiTheme: mockTheme,
1517
minutesPerAlert: 8,
@@ -18,6 +20,7 @@ describe('getCostSavingsMetricLensAttributes', () => {
1820
stackByField: undefined,
1921
esql: undefined,
2022
backgroundColor: '#00FF00',
23+
signalIndexName: defaultSignalIndexName,
2124
};
2225

2326
it('returns correct lens attributes with formula and filter handling', () => {
@@ -40,12 +43,40 @@ describe('getCostSavingsMetricLensAttributes', () => {
4043
...defaultParams,
4144
extraOptions: { filters },
4245
});
43-
expect(resultWithFilters.state.filters).toBe(filters);
46+
expect(resultWithFilters.state.filters).toEqual([
47+
getAlertIndexFilter(defaultSignalIndexName),
48+
...filters,
49+
]);
4450

4551
const resultWithoutFilters = getCostSavingsMetricLensAttributes({
4652
...defaultParams,
4753
extraOptions: undefined,
4854
});
49-
expect(resultWithoutFilters.state.filters).toEqual([]);
55+
expect(resultWithoutFilters.state.filters).toEqual([
56+
getAlertIndexFilter(defaultSignalIndexName),
57+
]);
58+
});
59+
60+
it('includes alert index filter in filters array', () => {
61+
const result = getCostSavingsMetricLensAttributes(defaultParams);
62+
const expectedFilter = getAlertIndexFilter(defaultSignalIndexName);
63+
expect(result.state.filters).toContainEqual(expectedFilter);
64+
});
65+
66+
it('handles different signal index names correctly', () => {
67+
const testCases = [
68+
'.alerts-security.alerts-default',
69+
'.alerts-security.alerts-custom-space',
70+
'custom-alerts-index',
71+
];
72+
73+
testCases.forEach((signalIndexName) => {
74+
const result = getCostSavingsMetricLensAttributes({
75+
...defaultParams,
76+
signalIndexName,
77+
});
78+
const expectedFilter = getAlertIndexFilter(signalIndexName);
79+
expect(result.state.filters).toContainEqual(expectedFilter);
80+
});
5081
});
5182
});

x-pack/solutions/security/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/ai/cost_savings_metric.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import type { EuiThemeComputed } from '@elastic/eui';
9+
import { getAlertIndexFilter } from './helpers';
910
import type { ExtraOptions, LensAttributes } from '../../types';
1011

1112
export type MyGetLensAttributes = (params: {
@@ -16,13 +17,15 @@ export type MyGetLensAttributes = (params: {
1617
backgroundColor: string;
1718
minutesPerAlert: number;
1819
analystHourlyRate: number;
20+
signalIndexName: string;
1921
}) => LensAttributes;
2022

2123
export const getCostSavingsMetricLensAttributes: MyGetLensAttributes = ({
2224
analystHourlyRate,
2325
backgroundColor,
2426
extraOptions,
2527
minutesPerAlert,
28+
signalIndexName,
2629
}) => {
2730
return {
2831
description: '',
@@ -97,7 +100,7 @@ export const getCostSavingsMetricLensAttributes: MyGetLensAttributes = ({
97100
},
98101
},
99102
},
100-
filters: extraOptions?.filters ?? [],
103+
filters: [getAlertIndexFilter(signalIndexName), ...(extraOptions?.filters ?? [])],
101104
internalReferences: [],
102105
query: { language: 'kuery', query: '' },
103106
visualization: {

x-pack/solutions/security/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/ai/cost_savings_trend_area.test.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,19 @@
77

88
import { getCostSavingsTrendAreaLensAttributes } from './cost_savings_trend_area';
99
import type { EuiThemeComputed } from '@elastic/eui';
10+
import { getAlertIndexFilter } from './helpers';
1011

1112
describe('getCostSavingsTrendAreaLensAttributes', () => {
1213
const mockTheme = {} as EuiThemeComputed;
14+
const defaultSignalIndexName = '.alerts-security.alerts-default';
1315
const defaultParams = {
1416
euiTheme: mockTheme,
1517
minutesPerAlert: 8,
1618
analystHourlyRate: 75,
1719
extraOptions: undefined,
1820
stackByField: undefined,
1921
esql: undefined,
22+
signalIndexName: defaultSignalIndexName,
2023
};
2124

2225
it('includes the correct formula in the cost column', () => {
@@ -38,15 +41,38 @@ describe('getCostSavingsTrendAreaLensAttributes', () => {
3841
...defaultParams,
3942
extraOptions: { filters },
4043
});
41-
expect(result.state.filters).toBe(filters);
44+
expect(result.state.filters).toEqual([getAlertIndexFilter(defaultSignalIndexName), ...filters]);
4245
});
4346

44-
it('defaults filters to empty array if extraOptions is not provided', () => {
47+
it('includes alert index filter even when extraOptions is not provided', () => {
4548
const result = getCostSavingsTrendAreaLensAttributes({
4649
...defaultParams,
4750
extraOptions: undefined,
4851
});
49-
expect(result.state.filters).toEqual([]);
52+
expect(result.state.filters).toEqual([getAlertIndexFilter(defaultSignalIndexName)]);
53+
});
54+
55+
it('includes alert index filter in filters array', () => {
56+
const result = getCostSavingsTrendAreaLensAttributes(defaultParams);
57+
const expectedFilter = getAlertIndexFilter(defaultSignalIndexName);
58+
expect(result.state.filters).toContainEqual(expectedFilter);
59+
});
60+
61+
it('handles different signal index names correctly', () => {
62+
const testCases = [
63+
'.alerts-security.alerts-default',
64+
'.alerts-security.alerts-custom-space',
65+
'custom-alerts-index',
66+
];
67+
68+
testCases.forEach((signalIndexName) => {
69+
const result = getCostSavingsTrendAreaLensAttributes({
70+
...defaultParams,
71+
signalIndexName,
72+
});
73+
const expectedFilter = getAlertIndexFilter(signalIndexName);
74+
expect(result.state.filters).toContainEqual(expectedFilter);
75+
});
5076
});
5177

5278
it('returns a LensAttributes object with required properties', () => {

x-pack/solutions/security/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/ai/cost_savings_trend_area.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import type { EuiThemeComputed } from '@elastic/eui';
9+
import { getAlertIndexFilter } from './helpers';
910
import type { ExtraOptions, LensAttributes } from '../../types';
1011

1112
const xColumn0 = 'cost_columnX0';
@@ -20,12 +21,14 @@ export type MyGetLensAttributes = (params: {
2021
esql?: string;
2122
minutesPerAlert: number;
2223
analystHourlyRate: number;
24+
signalIndexName: string;
2325
}) => LensAttributes;
2426

2527
export const getCostSavingsTrendAreaLensAttributes: MyGetLensAttributes = ({
2628
analystHourlyRate,
2729
extraOptions,
2830
minutesPerAlert,
31+
signalIndexName,
2932
}) => {
3033
return {
3134
description: '',
@@ -128,7 +131,7 @@ export const getCostSavingsTrendAreaLensAttributes: MyGetLensAttributes = ({
128131
},
129132
},
130133
},
131-
filters: extraOptions?.filters ?? [],
134+
filters: [getAlertIndexFilter(signalIndexName), ...(extraOptions?.filters ?? [])],
132135
internalReferences: [],
133136
query: {
134137
language: 'kuery',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { getAlertIndexFilter } from './helpers';
9+
10+
describe('getAlertIndexFilter', () => {
11+
const defaultSignalIndexName = '.alerts-security.alerts-default';
12+
const defaultIndexPatternId = 'indexpattern-datasource-layer-unifiedHistogram';
13+
14+
it('returns correct filter structure with default parameters', () => {
15+
const result = getAlertIndexFilter(defaultSignalIndexName);
16+
17+
expect(result).toEqual({
18+
meta: {
19+
disabled: false,
20+
negate: false,
21+
alias: null,
22+
index: defaultIndexPatternId,
23+
key: '_index',
24+
field: '_index',
25+
params: {
26+
query: defaultSignalIndexName,
27+
},
28+
type: 'phrase',
29+
},
30+
query: {
31+
match_phrase: {
32+
_index: defaultSignalIndexName,
33+
},
34+
},
35+
$state: {
36+
store: 'appState',
37+
},
38+
});
39+
});
40+
41+
it('returns correct filter structure with custom indexPatternId', () => {
42+
const customIndexPatternId = 'custom-index-pattern-id';
43+
const result = getAlertIndexFilter(defaultSignalIndexName, customIndexPatternId);
44+
45+
expect(result.meta.index).toBe(customIndexPatternId);
46+
expect(result.meta.params.query).toBe(defaultSignalIndexName);
47+
expect(result.query.match_phrase._index).toBe(defaultSignalIndexName);
48+
});
49+
50+
it('handles different signal index names correctly', () => {
51+
const testCases = [
52+
'.alerts-security.alerts-default',
53+
'.alerts-security.alerts-custom-space',
54+
'.alerts-security.alerts-space-123',
55+
'custom-alerts-index',
56+
];
57+
58+
testCases.forEach((signalIndexName) => {
59+
const result = getAlertIndexFilter(signalIndexName);
60+
61+
expect(result.meta.params.query).toBe(signalIndexName);
62+
expect(result.query.match_phrase._index).toBe(signalIndexName);
63+
expect(result.meta.key).toBe('_index');
64+
expect(result.meta.field).toBe('_index');
65+
});
66+
});
67+
68+
it('maintains consistent filter properties across different inputs', () => {
69+
const result = getAlertIndexFilter(defaultSignalIndexName);
70+
71+
expect(result.meta.disabled).toBe(false);
72+
expect(result.meta.negate).toBe(false);
73+
expect(result.meta.alias).toBe(null);
74+
expect(result.meta.type).toBe('phrase');
75+
expect(result.$state.store).toBe('appState');
76+
});
77+
78+
it('handles empty string signal index name', () => {
79+
const result = getAlertIndexFilter('');
80+
81+
expect(result.meta.params.query).toBe('');
82+
expect(result.query.match_phrase._index).toBe('');
83+
});
84+
});

0 commit comments

Comments
 (0)