mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[One Discover][Security Solution] Replace the use of Ecsflat with fieldsMetadata (#225105)
## Summary As per [comments](https://github.com/elastic/kibana/pull/204756#discussion_r2162038673) by @davismcphee , this PR removes the usage of `EcsFlat` and replaces it with `fieldsMetadata`.
This commit is contained in:
parent
401ddc0d56
commit
2ed4e8a341
5 changed files with 117 additions and 13 deletions
|
@ -16,6 +16,7 @@ export const ERROR_MESSAGE_FIELD = 'error.message';
|
||||||
export const EVENT_ORIGINAL_FIELD = 'event.original';
|
export const EVENT_ORIGINAL_FIELD = 'event.original';
|
||||||
export const EVENT_OUTCOME_FIELD = 'event.outcome';
|
export const EVENT_OUTCOME_FIELD = 'event.outcome';
|
||||||
export const INDEX_FIELD = '_index';
|
export const INDEX_FIELD = '_index';
|
||||||
|
export const EVENT_CATEGORY_FIELD = 'event.category';
|
||||||
|
|
||||||
// Trace fields
|
// Trace fields
|
||||||
export const TRACE_ID_FIELD = 'trace.id';
|
export const TRACE_ID_FIELD = 'trace.id';
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { fireEvent, render, screen } from '@testing-library/react';
|
||||||
import { AlertEventOverview } from './alert_event_overview';
|
import { AlertEventOverview } from './alert_event_overview';
|
||||||
import type { DataTableRecord } from '@kbn/discover-utils';
|
import type { DataTableRecord } from '@kbn/discover-utils';
|
||||||
import { dataViewMock } from '@kbn/discover-utils/src/__mocks__';
|
import { dataViewMock } from '@kbn/discover-utils/src/__mocks__';
|
||||||
import { EcsFlat } from '@elastic/ecs';
|
|
||||||
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
|
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
|
||||||
import { encode } from '@kbn/rison';
|
import { encode } from '@kbn/rison';
|
||||||
import { URLSearchParams } from 'url';
|
import { URLSearchParams } from 'url';
|
||||||
|
@ -27,6 +26,19 @@ const mockDiscoverServices = {
|
||||||
application: {
|
application: {
|
||||||
getUrlForApp: mockGetUrlForApp,
|
getUrlForApp: mockGetUrlForApp,
|
||||||
},
|
},
|
||||||
|
fieldsMetadata: {
|
||||||
|
useFieldsMetadata: jest.fn().mockReturnValue({
|
||||||
|
fieldsMetadata: {
|
||||||
|
'event.category': {
|
||||||
|
allowed_values: [
|
||||||
|
{ name: 'process', description: 'Process events' },
|
||||||
|
{ name: 'network', description: 'Network events' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockRow = {
|
const mockRow = {
|
||||||
|
@ -36,6 +48,7 @@ const mockRow = {
|
||||||
_id: 'test-id',
|
_id: 'test-id',
|
||||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||||
'kibana.alert.url': 'test-url',
|
'kibana.alert.url': 'test-url',
|
||||||
|
'event.category': 'process',
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockHit = {
|
const mockHit = {
|
||||||
|
@ -87,10 +100,7 @@ describe('AlertEventOverview', () => {
|
||||||
|
|
||||||
render(<AlertEventOverview hit={localMockHit} dataView={mockDataView} />);
|
render(<AlertEventOverview hit={localMockHit} dataView={mockDataView} />);
|
||||||
|
|
||||||
expect(screen.getByTestId('expandableContent-About')).toHaveTextContent(
|
expect(screen.getByTestId('expandableContent-About')).toHaveTextContent('Process events');
|
||||||
EcsFlat['event.category'].allowed_values.find((i) => i.name === 'process')
|
|
||||||
?.description as string
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should display timeline redirect url correctly', () => {
|
test('should display timeline redirect url correctly', () => {
|
||||||
|
@ -142,5 +152,73 @@ describe('AlertEventOverview', () => {
|
||||||
`test-timeline-url?${searchParams}`
|
`test-timeline-url?${searchParams}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('ECS Description', () => {
|
||||||
|
test('should give ECS description for event.category field', () => {
|
||||||
|
render(<AlertEventOverview hit={mockHit} dataView={mockDataView} />);
|
||||||
|
expect(screen.getByTestId('about')).toHaveTextContent('Process events');
|
||||||
|
});
|
||||||
|
test('should give placeholder ECS description when fieldsMetadata is not available', () => {
|
||||||
|
(useDiscoverServices as jest.Mock).mockReturnValue({
|
||||||
|
...mockDiscoverServices,
|
||||||
|
fieldsMetadata: {
|
||||||
|
useFieldsMetadata: jest.fn().mockReturnValue({
|
||||||
|
fieldsMetadata: undefined,
|
||||||
|
loading: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<AlertEventOverview hit={mockHit} dataView={mockDataView} />);
|
||||||
|
expect(screen.getByTestId('about')).toHaveTextContent(
|
||||||
|
"This field doesn't have a description because it's not part of ECS."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should give placeholder ECS description when event.category field is not present in fieldMetada', () => {
|
||||||
|
(useDiscoverServices as jest.Mock).mockReturnValue({
|
||||||
|
...mockDiscoverServices,
|
||||||
|
fieldsMetadata: {
|
||||||
|
useFieldsMetadata: jest.fn().mockReturnValue({
|
||||||
|
fieldsMetadata: {},
|
||||||
|
loading: false,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<AlertEventOverview hit={mockHit} dataView={mockDataView} />);
|
||||||
|
expect(screen.getByTestId('about')).toHaveTextContent(
|
||||||
|
"This field doesn't have a description because it's not part of ECS."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should give placeholder ECS description when event.category field is not present in hit', () => {
|
||||||
|
const localMockHit = {
|
||||||
|
flattened: {
|
||||||
|
...mockRow,
|
||||||
|
'event.category': undefined,
|
||||||
|
},
|
||||||
|
} as unknown as DataTableRecord;
|
||||||
|
|
||||||
|
render(<AlertEventOverview hit={localMockHit} dataView={mockDataView} />);
|
||||||
|
expect(screen.getByTestId('about')).toHaveTextContent(
|
||||||
|
"This field doesn't have a description because it's not part of ECS."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should give placeholder ECS when event.category fields has a value that is not in allowed values', () => {
|
||||||
|
const localMockHit = {
|
||||||
|
flattened: {
|
||||||
|
...mockRow,
|
||||||
|
'event.category': 'unknown',
|
||||||
|
},
|
||||||
|
} as unknown as DataTableRecord;
|
||||||
|
|
||||||
|
render(<AlertEventOverview hit={localMockHit} dataView={mockDataView} />);
|
||||||
|
expect(screen.getByTestId('about')).toHaveTextContent(
|
||||||
|
"This field doesn't have a description because it's not part of ECS."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import type { FC, PropsWithChildren } from 'react';
|
import type { FC, PropsWithChildren } from 'react';
|
||||||
import { getFieldValue } from '@kbn/discover-utils';
|
import { fieldConstants, getFieldValue } from '@kbn/discover-utils';
|
||||||
import type { DocViewerComponent } from '@kbn/unified-doc-viewer/src/services/types';
|
import type { DocViewerComponent } from '@kbn/unified-doc-viewer/src/services/types';
|
||||||
import {
|
import {
|
||||||
EuiTitle,
|
EuiTitle,
|
||||||
|
@ -19,6 +19,7 @@ import {
|
||||||
EuiFlexGroup,
|
EuiFlexGroup,
|
||||||
EuiFlexItem,
|
EuiFlexItem,
|
||||||
EuiText,
|
EuiText,
|
||||||
|
EuiSkeletonText,
|
||||||
} from '@elastic/eui';
|
} from '@elastic/eui';
|
||||||
import * as i18n from '../translations';
|
import * as i18n from '../translations';
|
||||||
import { getSecurityTimelineRedirectUrl } from '../utils';
|
import { getSecurityTimelineRedirectUrl } from '../utils';
|
||||||
|
@ -62,12 +63,18 @@ export const ExpandableSection: FC<PropsWithChildren<{ title: string }>> = ({
|
||||||
export const AlertEventOverview: DocViewerComponent = ({ hit }) => {
|
export const AlertEventOverview: DocViewerComponent = ({ hit }) => {
|
||||||
const {
|
const {
|
||||||
application: { getUrlForApp },
|
application: { getUrlForApp },
|
||||||
|
fieldsMetadata,
|
||||||
} = useDiscoverServices();
|
} = useDiscoverServices();
|
||||||
|
|
||||||
const timelinesURL = getUrlForApp('securitySolutionUI', {
|
const timelinesURL = getUrlForApp('securitySolutionUI', {
|
||||||
path: 'alerts',
|
path: 'alerts',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const result = fieldsMetadata?.useFieldsMetadata({
|
||||||
|
attributes: ['allowed_values', 'name', 'flat_name'],
|
||||||
|
fieldNames: [fieldConstants.EVENT_CATEGORY_FIELD],
|
||||||
|
});
|
||||||
|
|
||||||
const reason = useMemo(() => getFieldValue(hit, 'kibana.alert.reason') as string, [hit]);
|
const reason = useMemo(() => getFieldValue(hit, 'kibana.alert.reason') as string, [hit]);
|
||||||
const description = useMemo(
|
const description = useMemo(
|
||||||
() => getFieldValue(hit, 'kibana.alert.rule.description') as string,
|
() => getFieldValue(hit, 'kibana.alert.rule.description') as string,
|
||||||
|
@ -100,9 +107,18 @@ export const AlertEventOverview: DocViewerComponent = ({ hit }) => {
|
||||||
>
|
>
|
||||||
<EuiFlexItem>
|
<EuiFlexItem>
|
||||||
<ExpandableSection title={i18n.aboutSectionTitle}>
|
<ExpandableSection title={i18n.aboutSectionTitle}>
|
||||||
<EuiText size="s" data-test-subj="about">
|
{result?.loading ? (
|
||||||
{getEcsAllowedValueDescription(eventCategory)}
|
<EuiSkeletonText
|
||||||
</EuiText>
|
lines={2}
|
||||||
|
size={'s'}
|
||||||
|
isLoading={result?.loading}
|
||||||
|
contentAriaLabel={i18n.ecsDescriptionLoadingAriaLable}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<EuiText size="s" data-test-subj="about">
|
||||||
|
{getEcsAllowedValueDescription(result?.fieldsMetadata, eventCategory)}
|
||||||
|
</EuiText>
|
||||||
|
)}
|
||||||
</ExpandableSection>
|
</ExpandableSection>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
{description ? (
|
{description ? (
|
||||||
|
|
|
@ -54,3 +54,10 @@ export const reasonSectionTitle = i18n.translate(
|
||||||
defaultMessage: 'Reason',
|
defaultMessage: 'Reason',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const ecsDescriptionLoadingAriaLable = i18n.translate(
|
||||||
|
'discover.profile.security.flyout.ecsDescriptionLoadingAriaLabel',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Loading ECS description',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -7,9 +7,8 @@
|
||||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EcsFlat } from '@elastic/ecs';
|
import type { UseFieldsMetadataHook } from '@kbn/fields-metadata-plugin/public/hooks/use_fields_metadata';
|
||||||
import * as i18n from '../translations';
|
import * as i18n from '../translations';
|
||||||
export type EcsAllowedValue = (typeof EcsFlat)['event.category']['allowed_values'][0];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to return the description of an allowed value of the specified field
|
* Helper function to return the description of an allowed value of the specified field
|
||||||
|
@ -17,8 +16,11 @@ export type EcsAllowedValue = (typeof EcsFlat)['event.category']['allowed_values
|
||||||
* @param value
|
* @param value
|
||||||
* @returns ecs description of the value
|
* @returns ecs description of the value
|
||||||
*/
|
*/
|
||||||
export const getEcsAllowedValueDescription = (value: string): string => {
|
export const getEcsAllowedValueDescription = (
|
||||||
const allowedValues: EcsAllowedValue[] = EcsFlat['event.category']?.allowed_values ?? [];
|
fieldsMetadata: ReturnType<UseFieldsMetadataHook>['fieldsMetadata'] = {},
|
||||||
|
value: string
|
||||||
|
): string => {
|
||||||
|
const allowedValues = fieldsMetadata['event.category']?.allowed_values ?? [];
|
||||||
const result =
|
const result =
|
||||||
allowedValues?.find((item) => item.name === value)?.description ?? i18n.noEcsDescriptionReason;
|
allowedValues?.find((item) => item.name === value)?.description ?? i18n.noEcsDescriptionReason;
|
||||||
return result;
|
return result;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue