mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution] clean unused types, export components and add many unit tests (#217100)
## Summary This code change was originally part of [a bigger PR](https://github.com/elastic/kibana/pull/216744) related to the AI for SOC effort. I decided to split the work for 2 reasons: - less files to review, less teams impacted - this current PR will easily be backported to `8.x` while the AI for SOC is only targeting `9.1` This PR makes only a few small changes: - remove unused types - export a few components/functions outside of the `alerts_table` folder to make them reusable within the new AI for SOC alert summary page (see PR linked above) - add a lot of unit tests to everything, especially the now exported components/functions #### UI remains unchanged:     ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios Will unblock https://github.com/elastic/security-team/issues/11973
This commit is contained in:
parent
2ed4266ea5
commit
837059bcfa
13 changed files with 344 additions and 509 deletions
|
@ -9,6 +9,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiContextMenuItem } from '@elastic/eui';
|
||||
|
||||
export const host1Name = 'nice-host';
|
||||
export const host2Name = 'cool-host';
|
||||
|
||||
|
@ -48,9 +49,6 @@ export const mockGroupingProps = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
|
@ -81,9 +79,6 @@ export const mockGroupingProps = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
|
@ -115,9 +110,6 @@ export const mockGroupingProps = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 11,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 11,
|
||||
},
|
||||
|
|
|
@ -27,7 +27,6 @@ describe('group selector', () => {
|
|||
bucket_truncate: { bucket_sort: { from: 0, size: 25 } },
|
||||
alertsCount: { cardinality: { field: 'kibana.alert.uuid' } },
|
||||
rulesCountAggregation: { cardinality: { field: 'kibana.alert.rule.rule_id' } },
|
||||
countSeveritySubAggregation: { cardinality: { field: 'kibana.alert.severity' } },
|
||||
severitiesSubAggregation: { terms: { field: 'kibana.alert.severity' } },
|
||||
usersCountAggregation: { cardinality: { field: 'user.name' } },
|
||||
});
|
||||
|
|
|
@ -25,7 +25,6 @@ export const groupingBucket = {
|
|||
{ key: 'medium', doc_count: 480 },
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: { value: 4 },
|
||||
};
|
||||
|
||||
export const mocktestProps1: GroupingQueryArgs = {
|
||||
|
@ -50,13 +49,6 @@ export const mocktestProps1: GroupingQueryArgs = {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
|
|
|
@ -131,9 +131,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -182,9 +179,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -233,9 +227,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -284,9 +275,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -335,9 +323,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -386,9 +371,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -437,9 +419,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -488,9 +467,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -539,9 +515,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -590,9 +563,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -641,9 +611,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -692,9 +659,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -743,9 +707,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -794,9 +755,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -845,9 +803,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -896,9 +851,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -947,9 +899,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -998,9 +947,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1049,9 +995,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1100,9 +1043,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1151,9 +1091,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1202,9 +1139,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1253,9 +1187,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1304,9 +1235,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1355,9 +1283,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
|
|
@ -29,9 +29,9 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('should return values depending on the input field', () => {
|
||||
const ruleAggregations = defaultGroupStatsAggregations('kibana.alert.rule.name');
|
||||
expect(ruleAggregations).toEqual([
|
||||
it('should return values depending for kibana.alert.rule.name input field', () => {
|
||||
const aggregations = defaultGroupStatsAggregations('kibana.alert.rule.name');
|
||||
expect(aggregations).toEqual([
|
||||
{
|
||||
unitsCount: {
|
||||
cardinality: {
|
||||
|
@ -47,13 +47,6 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
|
@ -83,9 +76,11 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const hostAggregations = defaultGroupStatsAggregations('host.name');
|
||||
expect(hostAggregations).toEqual([
|
||||
it('should return values depending for host.name input field', () => {
|
||||
const aggregations = defaultGroupStatsAggregations('host.name');
|
||||
expect(aggregations).toEqual([
|
||||
{
|
||||
unitsCount: {
|
||||
cardinality: {
|
||||
|
@ -100,13 +95,6 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
|
@ -122,9 +110,11 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const userAggregations = defaultGroupStatsAggregations('user.name');
|
||||
expect(userAggregations).toEqual([
|
||||
it('should return values depending for user.name input field', () => {
|
||||
const aggregations = defaultGroupStatsAggregations('user.name');
|
||||
expect(aggregations).toEqual([
|
||||
{
|
||||
unitsCount: {
|
||||
cardinality: {
|
||||
|
@ -139,13 +129,6 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
|
@ -161,9 +144,11 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const sourceAggregations = defaultGroupStatsAggregations('source.ip');
|
||||
expect(sourceAggregations).toEqual([
|
||||
it('should return values depending for source.ip input field', () => {
|
||||
const aggregations = defaultGroupStatsAggregations('source.ip');
|
||||
expect(aggregations).toEqual([
|
||||
{
|
||||
unitsCount: {
|
||||
cardinality: {
|
||||
|
@ -178,13 +163,6 @@ describe('defaultGroupStatsAggregations', () => {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
|
|
|
@ -8,6 +8,35 @@
|
|||
import type { NamedAggregation } from '@kbn/grouping';
|
||||
import { DEFAULT_GROUP_STATS_AGGREGATION } from '../alerts_grouping';
|
||||
|
||||
export const SEVERITY_SUB_AGGREGATION = {
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
};
|
||||
export const USER_COUNT_AGGREGATION = {
|
||||
usersCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'user.name',
|
||||
},
|
||||
},
|
||||
};
|
||||
export const HOST_COUNT_AGGREGATION = {
|
||||
hostsCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'host.name',
|
||||
},
|
||||
},
|
||||
};
|
||||
export const RULE_COUNT_AGGREGATION = {
|
||||
rulesCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.rule.rule_id',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns aggregations to be used to calculate the statistics to be used in the `extraAction` property of the EUiAccordion component.
|
||||
* It handles custom renders for the following fields:
|
||||
|
@ -34,34 +63,9 @@ export const defaultGroupStatsAggregations = (field: string): NamedAggregation[]
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
usersCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'user.name',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
hostsCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'host.name',
|
||||
},
|
||||
},
|
||||
},
|
||||
SEVERITY_SUB_AGGREGATION,
|
||||
USER_COUNT_AGGREGATION,
|
||||
HOST_COUNT_AGGREGATION,
|
||||
{
|
||||
ruleTags: {
|
||||
terms: {
|
||||
|
@ -74,114 +78,21 @@ export const defaultGroupStatsAggregations = (field: string): NamedAggregation[]
|
|||
break;
|
||||
case 'host.name':
|
||||
aggMetrics.push(
|
||||
...[
|
||||
{
|
||||
rulesCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.rule.rule_id',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
usersCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'user.name',
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
...[RULE_COUNT_AGGREGATION, SEVERITY_SUB_AGGREGATION, USER_COUNT_AGGREGATION]
|
||||
);
|
||||
break;
|
||||
case 'user.name':
|
||||
aggMetrics.push(
|
||||
...[
|
||||
{
|
||||
rulesCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.rule.rule_id',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
hostsCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'host.name',
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
...[RULE_COUNT_AGGREGATION, SEVERITY_SUB_AGGREGATION, HOST_COUNT_AGGREGATION]
|
||||
);
|
||||
break;
|
||||
case 'source.ip':
|
||||
aggMetrics.push(
|
||||
...[
|
||||
{
|
||||
rulesCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.rule.rule_id',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
severitiesSubAggregation: {
|
||||
terms: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
hostsCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'host.name',
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
...[RULE_COUNT_AGGREGATION, SEVERITY_SUB_AGGREGATION, HOST_COUNT_AGGREGATION]
|
||||
);
|
||||
break;
|
||||
default:
|
||||
aggMetrics.push({
|
||||
rulesCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.rule.rule_id',
|
||||
},
|
||||
},
|
||||
});
|
||||
aggMetrics.push(RULE_COUNT_AGGREGATION);
|
||||
}
|
||||
return aggMetrics;
|
||||
};
|
||||
|
|
|
@ -5,27 +5,82 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { defaultGroupStatsRenderer } from '.';
|
||||
import { render } from '@testing-library/react';
|
||||
import { defaultGroupStatsRenderer, Severity } from '.';
|
||||
import React from 'react';
|
||||
import type { GenericBuckets } from '@kbn/grouping/src';
|
||||
|
||||
describe('getStats', () => {
|
||||
it('returns array of badges which corresponds to the field name', () => {
|
||||
const badgesRuleName = defaultGroupStatsRenderer('kibana.alert.rule.name', {
|
||||
key: [],
|
||||
describe('Severity', () => {
|
||||
it('should return a single low severity UI', () => {
|
||||
const buckets: GenericBuckets[] = [{ key: 'low', doc_count: 10 }];
|
||||
|
||||
const { getByText } = render(<Severity severities={buckets} />);
|
||||
|
||||
expect(getByText('Low')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return a single medium severity UI', () => {
|
||||
const buckets: GenericBuckets[] = [{ key: 'medium', doc_count: 10 }];
|
||||
|
||||
const { getByText } = render(<Severity severities={buckets} />);
|
||||
|
||||
expect(getByText('Medium')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return a single high severity UI', () => {
|
||||
const buckets: GenericBuckets[] = [{ key: 'high', doc_count: 10 }];
|
||||
|
||||
const { getByText } = render(<Severity severities={buckets} />);
|
||||
|
||||
expect(getByText('High')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return a single critical severity UI', () => {
|
||||
const buckets: GenericBuckets[] = [{ key: 'critical', doc_count: 10 }];
|
||||
|
||||
const { getByText } = render(<Severity severities={buckets} />);
|
||||
|
||||
expect(getByText('Critical')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return a single unknown severity UI', () => {
|
||||
const buckets: GenericBuckets[] = [{ key: '', doc_count: 10 }];
|
||||
|
||||
const { getByText } = render(<Severity severities={buckets} />);
|
||||
|
||||
expect(getByText('Unknown')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should return a multi severity UI', () => {
|
||||
const buckets: GenericBuckets[] = [
|
||||
{ key: 'low', doc_count: 10 },
|
||||
{ key: 'medium', doc_count: 10 },
|
||||
];
|
||||
|
||||
const { getByText } = render(<Severity severities={buckets} />);
|
||||
|
||||
expect(getByText('Multi')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultGroupStatsRenderer', () => {
|
||||
it('should return array of badges for kibana.alert.rule.name field', () => {
|
||||
const badges = defaultGroupStatsRenderer('kibana.alert.rule.name', {
|
||||
key: '',
|
||||
severitiesSubAggregation: { buckets: [{ key: 'medium', doc_count: 10 }] },
|
||||
countSeveritySubAggregation: { value: 1 },
|
||||
usersCountAggregation: { value: 3 },
|
||||
hostsCountAggregation: { value: 5 },
|
||||
doc_count: 10,
|
||||
});
|
||||
|
||||
expect(badgesRuleName.length).toBe(4);
|
||||
expect(badges.length).toBe(4);
|
||||
expect(
|
||||
badgesRuleName.find(
|
||||
badges.find(
|
||||
(badge) => badge.title === 'Severity:' && badge.component != null && badge.badge == null
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesRuleName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Users:' &&
|
||||
badge.component == null &&
|
||||
|
@ -34,7 +89,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesRuleName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Hosts:' &&
|
||||
badge.component == null &&
|
||||
|
@ -43,7 +98,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesRuleName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Alerts:' &&
|
||||
badge.component == null &&
|
||||
|
@ -51,24 +106,25 @@ describe('getStats', () => {
|
|||
badge.badge.value === 10
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
const badgesHostName = defaultGroupStatsRenderer('host.name', {
|
||||
key: 'Host',
|
||||
it('should return array of badges for host.name field', () => {
|
||||
const badges = defaultGroupStatsRenderer('host.name', {
|
||||
key: '',
|
||||
severitiesSubAggregation: { buckets: [{ key: 'medium', doc_count: 10 }] },
|
||||
countSeveritySubAggregation: { value: 1 },
|
||||
usersCountAggregation: { value: 5 },
|
||||
rulesCountAggregation: { value: 3 },
|
||||
doc_count: 2,
|
||||
});
|
||||
|
||||
expect(badgesHostName.length).toBe(4);
|
||||
expect(badges.length).toBe(4);
|
||||
expect(
|
||||
badgesHostName.find(
|
||||
badges.find(
|
||||
(badge) => badge.title === 'Severity:' && badge.component != null && badge.badge == null
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesHostName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Users:' &&
|
||||
badge.component == null &&
|
||||
|
@ -77,7 +133,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesHostName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Rules:' &&
|
||||
badge.component == null &&
|
||||
|
@ -86,7 +142,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesHostName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Alerts:' &&
|
||||
badge.component == null &&
|
||||
|
@ -94,24 +150,25 @@ describe('getStats', () => {
|
|||
badge.badge.value === 2
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
const badgesUserName = defaultGroupStatsRenderer('user.name', {
|
||||
key: 'User test',
|
||||
it('should return array of badges for user.name field', () => {
|
||||
const badges = defaultGroupStatsRenderer('user.name', {
|
||||
key: '',
|
||||
severitiesSubAggregation: { buckets: [{ key: 'medium', doc_count: 10 }] },
|
||||
countSeveritySubAggregation: { value: 1 },
|
||||
rulesCountAggregation: { value: 2 },
|
||||
hostsCountAggregation: { value: 1 },
|
||||
doc_count: 1,
|
||||
});
|
||||
|
||||
expect(badgesUserName.length).toBe(4);
|
||||
expect(badges.length).toBe(4);
|
||||
expect(
|
||||
badgesUserName.find(
|
||||
badges.find(
|
||||
(badge) => badge.title === 'Severity:' && badge.component != null && badge.badge == null
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesUserName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Hosts:' &&
|
||||
badge.component == null &&
|
||||
|
@ -120,7 +177,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesUserName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Rules:' &&
|
||||
badge.component == null &&
|
||||
|
@ -129,7 +186,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesUserName.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Alerts:' &&
|
||||
badge.component == null &&
|
||||
|
@ -137,24 +194,25 @@ describe('getStats', () => {
|
|||
badge.badge.value === 1
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
const badgesSourceIp = defaultGroupStatsRenderer('source.ip', {
|
||||
key: 'User test',
|
||||
it('should return array of badges for source.ip field', () => {
|
||||
const badges = defaultGroupStatsRenderer('source.ip', {
|
||||
key: '',
|
||||
severitiesSubAggregation: { buckets: [{ key: 'medium', doc_count: 10 }] },
|
||||
countSeveritySubAggregation: { value: 1 },
|
||||
rulesCountAggregation: { value: 16 },
|
||||
hostsCountAggregation: { value: 17 },
|
||||
doc_count: 18,
|
||||
});
|
||||
|
||||
expect(badgesSourceIp.length).toBe(4);
|
||||
expect(badges.length).toBe(4);
|
||||
expect(
|
||||
badgesSourceIp.find(
|
||||
badges.find(
|
||||
(badge) => badge.title === 'Severity:' && badge.component != null && badge.badge == null
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesSourceIp.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Hosts:' &&
|
||||
badge.component == null &&
|
||||
|
@ -163,7 +221,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesSourceIp.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Rules:' &&
|
||||
badge.component == null &&
|
||||
|
@ -172,7 +230,7 @@ describe('getStats', () => {
|
|||
)
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
badgesSourceIp.find(
|
||||
badges.find(
|
||||
(badge) =>
|
||||
badge.title === 'Alerts:' &&
|
||||
badge.component == null &&
|
||||
|
@ -184,9 +242,8 @@ describe('getStats', () => {
|
|||
|
||||
it('should return default badges if the field specific does not exist', () => {
|
||||
const badges = defaultGroupStatsRenderer('process.name', {
|
||||
key: 'process',
|
||||
key: '',
|
||||
severitiesSubAggregation: { buckets: [{ key: 'medium', doc_count: 10 }] },
|
||||
countSeveritySubAggregation: { value: 1 },
|
||||
rulesCountAggregation: { value: 3 },
|
||||
doc_count: 10,
|
||||
});
|
||||
|
|
|
@ -6,13 +6,67 @@
|
|||
*/
|
||||
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import type { GroupStatsItem, RawBucket } from '@kbn/grouping';
|
||||
import type { GenericBuckets } from '@kbn/grouping/src';
|
||||
import { DEFAULT_GROUP_STATS_RENDERER } from '../alerts_grouping';
|
||||
import type { AlertsGroupingAggregation } from './types';
|
||||
import * as i18n from '../translations';
|
||||
|
||||
const getSeverity = (severity?: string) => {
|
||||
export const getUsersBadge = (bucket: RawBucket<AlertsGroupingAggregation>) => ({
|
||||
title: i18n.STATS_GROUP_USERS,
|
||||
badge: {
|
||||
value: bucket.usersCountAggregation?.value ?? 0,
|
||||
},
|
||||
});
|
||||
export const getHostsBadge = (bucket: RawBucket<AlertsGroupingAggregation>) => ({
|
||||
title: i18n.STATS_GROUP_HOSTS,
|
||||
badge: {
|
||||
value: bucket.hostsCountAggregation?.value ?? 0,
|
||||
},
|
||||
});
|
||||
export const getRulesBadge = (bucket: RawBucket<AlertsGroupingAggregation>) => ({
|
||||
title: i18n.STATS_GROUP_RULES,
|
||||
badge: {
|
||||
value: bucket.rulesCountAggregation?.value ?? 0,
|
||||
},
|
||||
});
|
||||
|
||||
interface SingleSeverityProps {
|
||||
/**
|
||||
* Aggregation buckets for severities
|
||||
*/
|
||||
severities: GenericBuckets[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a colored icon and severity value (low, medium, high or critical) if only a single bucket is passed in.
|
||||
* If the value of the severity is null or incorrect, we return Unknown.
|
||||
* If there are multiple buckets, we return multiple icons.
|
||||
*/
|
||||
export const Severity = memo(({ severities }: SingleSeverityProps) => {
|
||||
if (severities.length > 1) {
|
||||
return (
|
||||
<>
|
||||
<span className="smallDot">
|
||||
<EuiIcon type="dot" color="#54b399" />
|
||||
</span>
|
||||
<span className="smallDot">
|
||||
<EuiIcon type="dot" color="#d6bf57" />
|
||||
</span>
|
||||
<span className="smallDot">
|
||||
<EuiIcon type="dot" color="#da8b45" />
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<EuiIcon type="dot" color="#e7664c" />
|
||||
</span>
|
||||
{i18n.STATS_GROUP_SEVERITY_MULTI}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const severity = severities[0].key;
|
||||
switch (severity) {
|
||||
case 'low':
|
||||
return (
|
||||
|
@ -42,29 +96,32 @@ const getSeverity = (severity?: string) => {
|
|||
{i18n.STATS_GROUP_SEVERITY_CRITICAL}
|
||||
</>
|
||||
);
|
||||
default:
|
||||
return <>{i18n.STATS_GROUP_SEVERITY_UNKNOWN}</>;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
Severity.displayName = 'Severity';
|
||||
|
||||
/**
|
||||
* Return a renderer for the severities aggregation.
|
||||
*/
|
||||
export const getSeverityComponent = (
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
): GroupStatsItem[] => {
|
||||
const severities = bucket.severitiesSubAggregation?.buckets;
|
||||
|
||||
if (!severities || severities.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
title: i18n.STATS_GROUP_SEVERITY,
|
||||
component: <Severity severities={severities} />,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
const multiSeverity = (
|
||||
<>
|
||||
<span className="smallDot">
|
||||
<EuiIcon type="dot" color="#54b399" />
|
||||
</span>
|
||||
<span className="smallDot">
|
||||
<EuiIcon type="dot" color="#d6bf57" />
|
||||
</span>
|
||||
<span className="smallDot">
|
||||
<EuiIcon type="dot" color="#da8b45" />
|
||||
</span>
|
||||
|
||||
<span>
|
||||
<EuiIcon type="dot" color="#e7664c" />
|
||||
</span>
|
||||
{i18n.STATS_GROUP_SEVERITY_MULTI}
|
||||
</>
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns statistics to be used in the`extraAction` property of the EuiAccordion component used within the kbn-grouping package.
|
||||
* It handles custom renders for the following fields:
|
||||
|
@ -80,88 +137,17 @@ export const defaultGroupStatsRenderer = (
|
|||
selectedGroup: string,
|
||||
bucket: RawBucket<AlertsGroupingAggregation>
|
||||
): GroupStatsItem[] => {
|
||||
const singleSeverityComponent =
|
||||
bucket.severitiesSubAggregation?.buckets && bucket.severitiesSubAggregation?.buckets?.length
|
||||
? getSeverity(bucket.severitiesSubAggregation?.buckets[0].key.toString())
|
||||
: null;
|
||||
const severityComponent =
|
||||
bucket.countSeveritySubAggregation?.value && bucket.countSeveritySubAggregation?.value > 1
|
||||
? multiSeverity
|
||||
: singleSeverityComponent;
|
||||
|
||||
const severityStat: GroupStatsItem[] = !severityComponent
|
||||
? []
|
||||
: [
|
||||
{
|
||||
title: i18n.STATS_GROUP_SEVERITY,
|
||||
component: severityComponent,
|
||||
},
|
||||
];
|
||||
|
||||
const severityStat: GroupStatsItem[] = getSeverityComponent(bucket);
|
||||
const defaultBadges: GroupStatsItem[] = DEFAULT_GROUP_STATS_RENDERER(selectedGroup, bucket);
|
||||
|
||||
switch (selectedGroup) {
|
||||
case 'kibana.alert.rule.name':
|
||||
return [
|
||||
...severityStat,
|
||||
{
|
||||
title: i18n.STATS_GROUP_USERS,
|
||||
badge: {
|
||||
value: bucket.usersCountAggregation?.value ?? 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18n.STATS_GROUP_HOSTS,
|
||||
badge: {
|
||||
value: bucket.hostsCountAggregation?.value ?? 0,
|
||||
},
|
||||
},
|
||||
...defaultBadges,
|
||||
];
|
||||
return [...severityStat, getUsersBadge(bucket), getHostsBadge(bucket), ...defaultBadges];
|
||||
case 'host.name':
|
||||
return [
|
||||
...severityStat,
|
||||
{
|
||||
title: i18n.STATS_GROUP_USERS,
|
||||
badge: {
|
||||
value: bucket.usersCountAggregation?.value ?? 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18n.STATS_GROUP_RULES,
|
||||
badge: {
|
||||
value: bucket.rulesCountAggregation?.value ?? 0,
|
||||
},
|
||||
},
|
||||
...defaultBadges,
|
||||
];
|
||||
return [...severityStat, getUsersBadge(bucket), getRulesBadge(bucket), ...defaultBadges];
|
||||
case 'user.name':
|
||||
case 'source.ip':
|
||||
return [
|
||||
...severityStat,
|
||||
{
|
||||
title: i18n.STATS_GROUP_HOSTS,
|
||||
badge: {
|
||||
value: bucket.hostsCountAggregation?.value ?? 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18n.STATS_GROUP_RULES,
|
||||
badge: {
|
||||
value: bucket.rulesCountAggregation?.value ?? 0,
|
||||
},
|
||||
},
|
||||
...defaultBadges,
|
||||
];
|
||||
return [...severityStat, getHostsBadge(bucket), getRulesBadge(bucket), ...defaultBadges];
|
||||
}
|
||||
return [
|
||||
...severityStat,
|
||||
{
|
||||
title: i18n.STATS_GROUP_RULES,
|
||||
badge: {
|
||||
value: bucket.rulesCountAggregation?.value ?? 0,
|
||||
},
|
||||
},
|
||||
...defaultBadges,
|
||||
];
|
||||
return [...severityStat, getRulesBadge(bucket), ...defaultBadges];
|
||||
};
|
||||
|
|
|
@ -5,12 +5,23 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { defaultGroupTitleRenderers } from '.';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
defaultGroupTitleRenderers,
|
||||
GroupWithIconContent,
|
||||
RULE_NAME_GROUP_DESCRIPTION_TEST_ID,
|
||||
RULE_NAME_GROUP_TAG_TEST_ID,
|
||||
RULE_NAME_GROUP_TAGS_TEST_ID,
|
||||
RULE_NAME_GROUP_TEST_ID,
|
||||
RULE_NAME_GROUP_TITLE_TEST_ID,
|
||||
RuleNameGroupContent,
|
||||
} from '.';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
describe('defaultGroupTitleRenderers', () => {
|
||||
it('renders correctly when the field renderer exists', () => {
|
||||
let { getByTestId } = render(
|
||||
it('should render correctly for kibana.alert.rule.name field', () => {
|
||||
const { getByTestId } = render(
|
||||
defaultGroupTitleRenderers(
|
||||
'kibana.alert.rule.name',
|
||||
{
|
||||
|
@ -21,8 +32,11 @@ describe('defaultGroupTitleRenderers', () => {
|
|||
)!
|
||||
);
|
||||
|
||||
expect(getByTestId('rule-name-group-renderer')).toBeInTheDocument();
|
||||
const result1 = render(
|
||||
expect(getByTestId(RULE_NAME_GROUP_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render correctly for host.name field', () => {
|
||||
const { getByTestId } = render(
|
||||
defaultGroupTitleRenderers(
|
||||
'host.name',
|
||||
{
|
||||
|
@ -32,11 +46,12 @@ describe('defaultGroupTitleRenderers', () => {
|
|||
'This is a null group!'
|
||||
)!
|
||||
);
|
||||
getByTestId = result1.getByTestId;
|
||||
|
||||
expect(getByTestId('host-name-group-renderer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const result2 = render(
|
||||
it('should render correctly for user.name field', () => {
|
||||
const { getByTestId } = render(
|
||||
defaultGroupTitleRenderers(
|
||||
'user.name',
|
||||
{
|
||||
|
@ -46,10 +61,12 @@ describe('defaultGroupTitleRenderers', () => {
|
|||
'This is a null group!'
|
||||
)!
|
||||
);
|
||||
getByTestId = result2.getByTestId;
|
||||
|
||||
expect(getByTestId('host-name-group-renderer')).toBeInTheDocument();
|
||||
const result3 = render(
|
||||
expect(getByTestId('user-name-group-renderer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render correctly for source.ip field', () => {
|
||||
const { getByTestId } = render(
|
||||
defaultGroupTitleRenderers(
|
||||
'source.ip',
|
||||
{
|
||||
|
@ -59,12 +76,11 @@ describe('defaultGroupTitleRenderers', () => {
|
|||
'This is a null group!'
|
||||
)!
|
||||
);
|
||||
getByTestId = result3.getByTestId;
|
||||
|
||||
expect(getByTestId('source-ip-group-renderer')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('returns undefined when the renderer does not exist', () => {
|
||||
it('should return undefined when the renderer does not exist', () => {
|
||||
const wrapper = defaultGroupTitleRenderers(
|
||||
'process.name',
|
||||
{
|
||||
|
@ -77,3 +93,60 @@ describe('defaultGroupTitleRenderers', () => {
|
|||
expect(wrapper).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('RuleNameGroupContent', () => {
|
||||
it('should render component', () => {
|
||||
const { getByTestId, queryByTestId } = render(
|
||||
<RuleNameGroupContent ruleName="rule_name" ruleDescription="rule_description" />
|
||||
);
|
||||
|
||||
expect(getByTestId(RULE_NAME_GROUP_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId(RULE_NAME_GROUP_TITLE_TEST_ID)).toHaveTextContent('rule_name');
|
||||
expect(getByTestId(RULE_NAME_GROUP_DESCRIPTION_TEST_ID)).toHaveTextContent('rule_description');
|
||||
expect(queryByTestId(RULE_NAME_GROUP_TAGS_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(RULE_NAME_GROUP_TAG_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render component with tags', () => {
|
||||
const { getByTestId } = render(
|
||||
<RuleNameGroupContent
|
||||
ruleName="rule_name"
|
||||
ruleDescription="rule_description"
|
||||
tags={[
|
||||
{
|
||||
key: 'key',
|
||||
doc_count: 2,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByTestId(RULE_NAME_GROUP_TAGS_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GroupWithIconContent', () => {
|
||||
it('should render component with icon', () => {
|
||||
const { getByTestId, queryByTestId } = render(
|
||||
<GroupWithIconContent title="title" icon="icon" dataTestSubj="test_id" />
|
||||
);
|
||||
|
||||
expect(getByTestId('test_id-group-renderer')).toBeInTheDocument();
|
||||
expect(getByTestId('test_id-group-renderer-icon')).toBeInTheDocument();
|
||||
expect(getByTestId('test_id-group-renderer-title')).toHaveTextContent('title');
|
||||
expect(queryByTestId('test_id-group-renderer-null-message')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render null message information icon', () => {
|
||||
const { getByTestId } = render(
|
||||
<GroupWithIconContent
|
||||
title="title"
|
||||
icon="icon"
|
||||
nullGroupMessage="null_message"
|
||||
dataTestSubj="test_id"
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getByTestId('test_id-group-renderer-null-message')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -53,7 +53,7 @@ export const defaultGroupTitleRenderers: GroupPanelRenderer<AlertsGroupingAggreg
|
|||
) : undefined;
|
||||
case 'host.name':
|
||||
return (
|
||||
<GroupContent
|
||||
<GroupWithIconContent
|
||||
title={bucket.key}
|
||||
icon="storage"
|
||||
nullGroupMessage={nullGroupMessage}
|
||||
|
@ -62,7 +62,7 @@ export const defaultGroupTitleRenderers: GroupPanelRenderer<AlertsGroupingAggreg
|
|||
);
|
||||
case 'user.name':
|
||||
return (
|
||||
<GroupContent
|
||||
<GroupWithIconContent
|
||||
title={bucket.key}
|
||||
icon="user"
|
||||
nullGroupMessage={nullGroupMessage}
|
||||
|
@ -71,7 +71,7 @@ export const defaultGroupTitleRenderers: GroupPanelRenderer<AlertsGroupingAggreg
|
|||
);
|
||||
case 'source.ip':
|
||||
return (
|
||||
<GroupContent
|
||||
<GroupWithIconContent
|
||||
title={bucket.key}
|
||||
icon="globe"
|
||||
nullGroupMessage={nullGroupMessage}
|
||||
|
@ -81,21 +81,27 @@ export const defaultGroupTitleRenderers: GroupPanelRenderer<AlertsGroupingAggreg
|
|||
}
|
||||
};
|
||||
|
||||
const RuleNameGroupContent = React.memo<{
|
||||
export const RULE_NAME_GROUP_TEST_ID = 'rule-name-group-renderer';
|
||||
export const RULE_NAME_GROUP_TITLE_TEST_ID = 'rule-name-group-renderer-title';
|
||||
export const RULE_NAME_GROUP_DESCRIPTION_TEST_ID = 'rule-name-group-renderer-description';
|
||||
export const RULE_NAME_GROUP_TAG_TEST_ID = 'rule-name-group-renderer-tag';
|
||||
export const RULE_NAME_GROUP_TAGS_TEST_ID = 'rule-name-group-renderer-tags';
|
||||
|
||||
export const RuleNameGroupContent = React.memo<{
|
||||
ruleName: string;
|
||||
ruleDescription: string;
|
||||
tags?: GenericBuckets[] | undefined;
|
||||
}>(({ ruleName, ruleDescription, tags }) => {
|
||||
const renderItem = (tag: string, i: number) => (
|
||||
<EuiBadge color="hollow" key={`${tag}-${i}`} data-test-subj="tag">
|
||||
<EuiBadge color="hollow" key={`${tag}-${i}`} data-test-subj={RULE_NAME_GROUP_TAG_TEST_ID}>
|
||||
{tag}
|
||||
</EuiBadge>
|
||||
);
|
||||
return (
|
||||
<div style={{ display: 'table', tableLayout: 'fixed', width: '100%' }}>
|
||||
<EuiFlexGroup data-test-subj="rule-name-group-renderer" gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false} style={{ display: 'contents' }}>
|
||||
<EuiTitle size="xs">
|
||||
<EuiFlexGroup data-test-subj={RULE_NAME_GROUP_TEST_ID} gutterSize="s" alignItems="center">
|
||||
<EuiFlexItem grow={false} css={{ display: 'contents' }}>
|
||||
<EuiTitle data-test-subj={RULE_NAME_GROUP_TITLE_TEST_ID} size="xs">
|
||||
<h5 className="eui-textTruncate">{ruleName.trim()}</h5>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
|
@ -106,14 +112,13 @@ const RuleNameGroupContent = React.memo<{
|
|||
popoverTitle={COLUMN_TAGS}
|
||||
popoverButtonTitle={tags.length.toString()}
|
||||
popoverButtonIcon="tag"
|
||||
dataTestPrefix="tags"
|
||||
dataTestPrefix={RULE_NAME_GROUP_TAGS_TEST_ID}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiText size="s">
|
||||
<EuiText data-test-subj={RULE_NAME_GROUP_DESCRIPTION_TEST_ID} size="s">
|
||||
<p className="eui-textTruncate">
|
||||
<EuiTextColor color="subdued">{ruleDescription}</EuiTextColor>
|
||||
</p>
|
||||
|
@ -123,7 +128,7 @@ const RuleNameGroupContent = React.memo<{
|
|||
});
|
||||
RuleNameGroupContent.displayName = 'RuleNameGroup';
|
||||
|
||||
const GroupContent = React.memo<{
|
||||
export const GroupWithIconContent = React.memo<{
|
||||
title: string | string[];
|
||||
icon: string;
|
||||
nullGroupMessage?: string;
|
||||
|
@ -135,18 +140,18 @@ const GroupContent = React.memo<{
|
|||
alignItems="center"
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type={icon} size="m" />
|
||||
<EuiIcon data-test-subj={`${dataTestSubj}-group-renderer-icon`} size="m" type={icon} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<EuiTitle data-test-subj={`${dataTestSubj}-group-renderer-title`} size="xs">
|
||||
<h5>{title}</h5>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
{nullGroupMessage && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexItem data-test-subj={`${dataTestSubj}-group-renderer-null-message`} grow={false}>
|
||||
<EuiIconTip content={nullGroupMessage} position="right" />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
));
|
||||
GroupContent.displayName = 'GroupContent';
|
||||
GroupWithIconContent.displayName = 'GroupWithIconContent';
|
||||
|
|
|
@ -50,11 +50,6 @@ export const getQuery = (
|
|||
],
|
||||
},
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
cardinality: {
|
||||
field: 'kibana.alert.severity',
|
||||
},
|
||||
},
|
||||
hostsCountAggregation: {
|
||||
cardinality: {
|
||||
field: 'host.name',
|
||||
|
@ -175,9 +170,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -226,9 +218,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -277,9 +266,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -328,9 +314,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -379,9 +362,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -430,9 +410,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -481,9 +458,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -532,9 +506,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -583,9 +554,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -634,9 +602,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -685,9 +650,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -736,9 +698,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -787,9 +746,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -838,9 +794,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -889,9 +842,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -940,9 +890,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
@ -991,9 +938,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1042,9 +986,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1093,9 +1034,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1144,9 +1082,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1195,9 +1130,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1246,9 +1178,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1297,9 +1226,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1348,9 +1274,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 0,
|
||||
},
|
||||
|
@ -1399,9 +1322,6 @@ export const groupingSearchResponse = {
|
|||
},
|
||||
],
|
||||
},
|
||||
countSeveritySubAggregation: {
|
||||
value: 1,
|
||||
},
|
||||
usersCountAggregation: {
|
||||
value: 91,
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import type { GenericBuckets } from '@kbn/grouping/src';
|
||||
// Elasticsearch returns `null` when a sub-aggregation cannot be computed
|
||||
type NumberOrNull = number | null;
|
||||
|
||||
export interface AlertsGroupingAggregation {
|
||||
unitsCount?: {
|
||||
value?: NumberOrNull;
|
||||
|
@ -18,18 +19,12 @@ export interface AlertsGroupingAggregation {
|
|||
severitiesSubAggregation?: {
|
||||
buckets?: GenericBuckets[];
|
||||
};
|
||||
countSeveritySubAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
usersCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
hostsCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
ipsCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
rulesCountAggregation?: {
|
||||
value?: NumberOrNull;
|
||||
};
|
||||
|
@ -38,9 +33,4 @@ export interface AlertsGroupingAggregation {
|
|||
sum_other_doc_count?: number;
|
||||
buckets?: GenericBuckets[];
|
||||
};
|
||||
stackByMultipleFields1?: {
|
||||
buckets?: GenericBuckets[];
|
||||
doc_count_error_upper_bound?: number;
|
||||
sum_other_doc_count?: number;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -393,6 +393,13 @@ export const STATS_GROUP_SEVERITY_MEDIUM = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const STATS_GROUP_SEVERITY_UNKNOWN = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.groups.stats.severity.unknown',
|
||||
{
|
||||
defaultMessage: 'Unknown',
|
||||
}
|
||||
);
|
||||
|
||||
export const INSPECT_GROUPING_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.detectionsEngine.grouping.inspectTitle',
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue