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_OUTCOME_FIELD = 'event.outcome';
|
||||
export const INDEX_FIELD = '_index';
|
||||
export const EVENT_CATEGORY_FIELD = 'event.category';
|
||||
|
||||
// Trace fields
|
||||
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 type { DataTableRecord } from '@kbn/discover-utils';
|
||||
import { dataViewMock } from '@kbn/discover-utils/src/__mocks__';
|
||||
import { EcsFlat } from '@elastic/ecs';
|
||||
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
|
||||
import { encode } from '@kbn/rison';
|
||||
import { URLSearchParams } from 'url';
|
||||
|
@ -27,6 +26,19 @@ const mockDiscoverServices = {
|
|||
application: {
|
||||
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 = {
|
||||
|
@ -36,6 +48,7 @@ const mockRow = {
|
|||
_id: 'test-id',
|
||||
'@timestamp': '2021-08-02T14:00:00.000Z',
|
||||
'kibana.alert.url': 'test-url',
|
||||
'event.category': 'process',
|
||||
};
|
||||
|
||||
const mockHit = {
|
||||
|
@ -87,10 +100,7 @@ describe('AlertEventOverview', () => {
|
|||
|
||||
render(<AlertEventOverview hit={localMockHit} dataView={mockDataView} />);
|
||||
|
||||
expect(screen.getByTestId('expandableContent-About')).toHaveTextContent(
|
||||
EcsFlat['event.category'].allowed_values.find((i) => i.name === 'process')
|
||||
?.description as string
|
||||
);
|
||||
expect(screen.getByTestId('expandableContent-About')).toHaveTextContent('Process events');
|
||||
});
|
||||
|
||||
test('should display timeline redirect url correctly', () => {
|
||||
|
@ -142,5 +152,73 @@ describe('AlertEventOverview', () => {
|
|||
`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 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 {
|
||||
EuiTitle,
|
||||
|
@ -19,6 +19,7 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiText,
|
||||
EuiSkeletonText,
|
||||
} from '@elastic/eui';
|
||||
import * as i18n from '../translations';
|
||||
import { getSecurityTimelineRedirectUrl } from '../utils';
|
||||
|
@ -62,12 +63,18 @@ export const ExpandableSection: FC<PropsWithChildren<{ title: string }>> = ({
|
|||
export const AlertEventOverview: DocViewerComponent = ({ hit }) => {
|
||||
const {
|
||||
application: { getUrlForApp },
|
||||
fieldsMetadata,
|
||||
} = useDiscoverServices();
|
||||
|
||||
const timelinesURL = getUrlForApp('securitySolutionUI', {
|
||||
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 description = useMemo(
|
||||
() => getFieldValue(hit, 'kibana.alert.rule.description') as string,
|
||||
|
@ -100,9 +107,18 @@ export const AlertEventOverview: DocViewerComponent = ({ hit }) => {
|
|||
>
|
||||
<EuiFlexItem>
|
||||
<ExpandableSection title={i18n.aboutSectionTitle}>
|
||||
<EuiText size="s" data-test-subj="about">
|
||||
{getEcsAllowedValueDescription(eventCategory)}
|
||||
</EuiText>
|
||||
{result?.loading ? (
|
||||
<EuiSkeletonText
|
||||
lines={2}
|
||||
size={'s'}
|
||||
isLoading={result?.loading}
|
||||
contentAriaLabel={i18n.ecsDescriptionLoadingAriaLable}
|
||||
/>
|
||||
) : (
|
||||
<EuiText size="s" data-test-subj="about">
|
||||
{getEcsAllowedValueDescription(result?.fieldsMetadata, eventCategory)}
|
||||
</EuiText>
|
||||
)}
|
||||
</ExpandableSection>
|
||||
</EuiFlexItem>
|
||||
{description ? (
|
||||
|
|
|
@ -54,3 +54,10 @@ export const reasonSectionTitle = i18n.translate(
|
|||
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".
|
||||
*/
|
||||
|
||||
import { EcsFlat } from '@elastic/ecs';
|
||||
import type { UseFieldsMetadataHook } from '@kbn/fields-metadata-plugin/public/hooks/use_fields_metadata';
|
||||
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
|
||||
|
@ -17,8 +16,11 @@ export type EcsAllowedValue = (typeof EcsFlat)['event.category']['allowed_values
|
|||
* @param value
|
||||
* @returns ecs description of the value
|
||||
*/
|
||||
export const getEcsAllowedValueDescription = (value: string): string => {
|
||||
const allowedValues: EcsAllowedValue[] = EcsFlat['event.category']?.allowed_values ?? [];
|
||||
export const getEcsAllowedValueDescription = (
|
||||
fieldsMetadata: ReturnType<UseFieldsMetadataHook>['fieldsMetadata'] = {},
|
||||
value: string
|
||||
): string => {
|
||||
const allowedValues = fieldsMetadata['event.category']?.allowed_values ?? [];
|
||||
const result =
|
||||
allowedValues?.find((item) => item.name === value)?.description ?? i18n.noEcsDescriptionReason;
|
||||
return result;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue