Skip to content

Commit

Permalink
[CTI] updates Alert Summary UI (#107081)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecezalp authored and kibanamachine committed Aug 3, 2021
1 parent 7ed8d5a commit b3774e8
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import { getEmptyValue } from '../empty_value';
import { ActionCell } from './table/action_cell';
import { FieldValueCell } from './table/field_value_cell';
import { TimelineEventsDetailsItem } from '../../../../common';
import { EventFieldsData } from './types';

export const Indent = styled.div`
padding: 0 8px;
Expand Down Expand Up @@ -95,23 +94,19 @@ const getDescription = ({
return <StyledEmptyComponent>{getEmptyValue()}</StyledEmptyComponent>;
}

const eventFieldsData = {
...data,
...(fieldFromBrowserField ? fieldFromBrowserField : {}),
} as EventFieldsData;
return (
<>
<FieldValueCell
contextId={timelineId}
data={eventFieldsData}
data={data}
eventId={eventId}
fieldFromBrowserField={fieldFromBrowserField}
linkValue={linkValue}
values={values}
/>
<ActionCell
contextId={timelineId}
data={eventFieldsData}
data={data}
eventId={eventId}
fieldFromBrowserField={fieldFromBrowserField}
linkValue={linkValue}
Expand Down Expand Up @@ -178,7 +173,13 @@ const getSummaryRows = ({
const browserField = get([category, 'fields', fieldName], browserFields);
const description = {
...initialDescription,
data: { ...field, ...(item.overrideField ? { field: item.overrideField } : {}) },
data: {
field: field.field,
format: browserField?.format ?? '',
type: browserField?.type ?? '',
isObjectArray: field.isObjectArray,
...(item.overrideField ? { field: item.overrideField } : {}),
},
values: field.values,
linkValue: linkValue ?? undefined,
fieldFromBrowserField: browserField,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { BrowserFields } from '../../containers/source';
import { OnUpdateColumns } from '../../../timelines/components/timeline/events';
import * as i18n from './translations';
import { EventFieldsData } from './types';
import { ColumnHeaderOptions } from '../../../../common';
import { BrowserField, ColumnHeaderOptions } from '../../../../common';
import { FieldValueCell } from './table/field_value_cell';
import { FieldNameCell } from './table/field_name_cell';
import { ActionCell } from './table/action_cell';
Expand All @@ -36,7 +36,7 @@ const HoverActionsContainer = styled(EuiPanel)`
HoverActionsContainer.displayName = 'HoverActionsContainer';

export const getFieldFromBrowserField = memoizeOne(
(keys: string[], browserFields: BrowserFields): BrowserFields => get(browserFields, keys),
(keys: string[], browserFields: BrowserFields): BrowserField => get(browserFields, keys),
(newArgs, lastArgs) => newArgs[0].join() === lastArgs[0].join()
);
export const getColumns = ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ const ThreatDetailsViewComponent: React.FC<{
<h5>{i18n.INVESTIGATION_TOOLTIP_TITLE}</h5>
</EuiTitle>
<EuiSpacer size="xs" />
{/* TODO: Date form */}
<EuiText size="xs">
<FormattedMessage
id="xpack.securitySolution.alertDetails.threatDetails.investigationSubtitle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,34 @@ import { ThreatSummaryView } from './threat_summary_view';
import { TestProviders } from '../../../mock';
import { useMountAppended } from '../../../utils/use_mount_appended';
import { buildEventEnrichmentMock } from '../../../../../common/search_strategy/security_solution/cti/index.mock';
import { mockAlertDetailsData } from '../__mocks__';
import { TimelineEventsDetailsItem } from '../../../../../../timelines/common';
import { mockBrowserFields } from '../../../containers/source/mock';

jest.mock('../../../../timelines/components/timeline/body/renderers/formatted_field');
jest.mock('../table/action_cell');
jest.mock('../table/field_name_cell');

describe('ThreatSummaryView', () => {
const mount = useMountAppended();
const eventId = '5d1d53da502f56aacc14c3cb5c669363d102b31f99822e5d369d4804ed370a31';
const timelineId = 'detections-page';

beforeEach(() => {
jest.clearAllMocks();
});
const data = mockAlertDetailsData as TimelineEventsDetailsItem[];
const browserFields = mockBrowserFields;

it('renders a row for each enrichment', () => {
const enrichments = [
buildEventEnrichmentMock(),
buildEventEnrichmentMock({ 'matched.id': ['test.id'], 'matched.field': ['test.field'] }),
buildEventEnrichmentMock({ 'matched.id': ['other.id'], 'matched.field': ['other.field'] }),
];
const wrapper = mount(
<TestProviders>
<ThreatSummaryView enrichments={enrichments} eventId={eventId} timelineId={timelineId} />
<ThreatSummaryView
data={data}
browserFields={browserFields}
enrichments={enrichments}
eventId={eventId}
timelineId={timelineId}
/>
</TestProviders>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,33 @@
*/

import styled from 'styled-components';
import React from 'react';
import { get } from 'lodash/fp';
import React, { Fragment } from 'react';
import { EuiBasicTableColumn, EuiText, EuiTitle } from '@elastic/eui';

import * as i18n from './translations';
import { Indent, StyledEuiInMemoryTable } from '../summary_view';
import { FormattedFieldValue } from '../../../../timelines/components/timeline/body/renderers/formatted_field';
import { CtiEnrichment } from '../../../../../common/search_strategy/security_solution/cti';
import { getEnrichmentIdentifiers } from './helpers';
import { EnrichmentIcon } from './enrichment_icon';
import { FieldsData } from '../types';
import { ActionCell } from '../table/action_cell';
import { BrowserField, BrowserFields, TimelineEventsDetailsItem } from '../../../../../common';
import { FieldValueCell } from '../table/field_value_cell';

export interface ThreatSummaryItem {
title: {
title: string | undefined;
type: string | undefined;
};
description: {
timelineId: string;
browserField: BrowserField;
data: FieldsData | undefined;
eventId: string;
fieldName: string | undefined;
index: number;
value: string | undefined;
provider: string | undefined;
timelineId: string;
value: string | undefined;
};
}

Expand All @@ -48,24 +53,25 @@ const EnrichmentTitle: React.FC<ThreatSummaryItem['title']> = ({ title, type })
);

const EnrichmentDescription: React.FC<ThreatSummaryItem['description']> = ({
timelineId,
browserField,
data,
eventId,
fieldName,
index,
value,
provider,
timelineId,
value,
}) => {
const key = `alert-details-value-formatted-field-value-${timelineId}-${eventId}-${fieldName}-${value}-${index}-${provider}`;
if (!data || !value) return null;
const key = `alert-details-value-formatted-field-value-${timelineId}-${eventId}-${data.field}-${value}-${index}-${provider}`;
return (
<>
<Fragment key={key}>
<RightMargin>
<FormattedFieldValue
key={key}
contextId={key}
eventId={eventId}
isDraggable={false}
fieldName={fieldName || 'unknown'}
value={value}
<FieldValueCell
contextId={timelineId}
data={data}
eventId={key}
fieldFromBrowserField={browserField}
values={[value]}
/>
</RightMargin>
{provider && (
Expand All @@ -82,17 +88,39 @@ const EnrichmentDescription: React.FC<ThreatSummaryItem['description']> = ({
</RightMargin>
</>
)}
</>
{value && (
<ActionCell
data={data}
contextId={timelineId}
eventId={key}
fieldFromBrowserField={browserField}
timelineId={timelineId}
values={[value]}
/>
)}
</Fragment>
);
};

const buildThreatSummaryItems = (
browserFields: BrowserFields,
data: TimelineEventsDetailsItem[],
enrichments: CtiEnrichment[],
timelineId: string,
eventId: string
) => {
return enrichments.map((enrichment, index) => {
const { field, type, value, provider } = getEnrichmentIdentifiers(enrichment);
const eventData = data.find((item) => item.field === field);
const category = eventData?.category ?? '';
const browserField = get([category, 'fields', field ?? ''], browserFields);

const fieldsData = {
field,
format: browserField?.format ?? '',
type: browserField?.type ?? '',
isObjectArray: eventData?.isObjectArray,
};

return {
title: {
Expand All @@ -101,11 +129,12 @@ const buildThreatSummaryItems = (
},
description: {
eventId,
fieldName: field,
index,
provider,
timelineId,
value,
data: fieldsData,
browserField,
},
};
});
Expand All @@ -116,7 +145,7 @@ const columns: Array<EuiBasicTableColumn<ThreatSummaryItem>> = [
field: 'title',
truncateText: false,
render: EnrichmentTitle,
width: '160px',
width: '220px',
name: '',
},
{
Expand All @@ -131,13 +160,15 @@ const ThreatSummaryViewComponent: React.FC<{
enrichments: CtiEnrichment[];
timelineId: string;
eventId: string;
}> = ({ enrichments, timelineId, eventId }) => (
data: TimelineEventsDetailsItem[];
browserFields: BrowserFields;
}> = ({ enrichments, timelineId, eventId, data, browserFields }) => (
<Indent>
<StyledEuiInMemoryTable
columns={columns}
compressed
data-test-subj="threat-summary-view"
items={buildThreatSummaryItems(enrichments, timelineId, eventId)}
items={buildThreatSummaryItems(browserFields, data, enrichments, timelineId, eventId)}
/>
</Indent>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ const EventDetailsComponent: React.FC<Props> = ({
/>
{enrichmentCount > 0 && (
<ThreatSummaryView
browserFields={browserFields}
data={data}
eventId={id}
timelineId={timelineId}
enrichments={allEnrichments}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { get, getOr, isEmpty, uniqBy } from 'lodash/fp';

import styled from 'styled-components';
import React from 'react';
import { EuiBasicTableColumn, EuiTitle } from '@elastic/eui';
import {
Expand All @@ -21,9 +22,10 @@ import {
DEFAULT_DATE_COLUMN_MIN_WIDTH,
DEFAULT_COLUMN_MIN_WIDTH,
} from '../../../timelines/components/timeline/body/constants';
import { FieldsData } from './types';

import * as i18n from './translations';
import { ColumnHeaderOptions, TimelineEventsDetailsItem } from '../../../../common';
import { ColumnHeaderOptions } from '../../../../common';

/**
* Defines the behavior of the search input that appears above the table of data
Expand Down Expand Up @@ -55,9 +57,9 @@ export interface Item {
export interface AlertSummaryRow {
title: string;
description: {
data: TimelineEventsDetailsItem;
data: FieldsData;
eventId: string;
fieldFromBrowserField?: Readonly<Record<string, Partial<BrowserField>>>;
fieldFromBrowserField?: BrowserField;
linkValue: string | undefined;
timelineId: string;
values: string[] | null | undefined;
Expand Down Expand Up @@ -196,9 +198,13 @@ export const onEventDetailsTabKeyPressed = ({
}
};

const StyledH5 = styled.h5`
line-height: 1.7rem;
`;

const getTitle = (title: string) => (
<EuiTitle size="xxxs">
<h5>{title}</h5>
<StyledH5>{title}</StyledH5>
</EuiTitle>
);
getTitle.displayName = 'getTitle';
Expand All @@ -213,7 +219,7 @@ export const getSummaryColumns = (
field: 'title',
truncateText: false,
render: getTitle,
width: '33%',
width: '220px',
name: '',
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
import React, { useCallback, useState, useRef } from 'react';
import { HoverActions } from '../../hover_actions';
import { useActionCellDataProvider } from './use_action_cell_data_provider';
import { EventFieldsData } from '../types';
import { EventFieldsData, FieldsData } from '../types';
import { useGetTimelineId } from '../../drag_and_drop/use_get_timeline_id_from_dom';
import { ColumnHeaderOptions } from '../../../../../common/types/timeline';
import { BrowserField } from '../../../containers/source';

interface Props {
contextId: string;
data: EventFieldsData;
data: FieldsData | EventFieldsData;
disabled?: boolean;
eventId: string;
fieldFromBrowserField?: Readonly<Record<string, Partial<BrowserField>>>;
fieldFromBrowserField?: BrowserField;
getLinkValue?: (field: string) => string | null;
linkValue?: string | null | undefined;
onFilterAdded?: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { getFieldTypeName } from './get_field_type_name';
export interface FieldNameCellProps {
data: EventFieldsData;
field: string;
fieldFromBrowserField: Readonly<Record<string, Partial<BrowserField>>>;
fieldFromBrowserField: BrowserField;
fieldMapping?: IndexPatternField;
scripted?: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ import { BrowserField } from '../../../containers/source';
import { OverflowField } from '../../tables/helpers';
import { FormattedFieldValue } from '../../../../timelines/components/timeline/body/renderers/formatted_field';
import { MESSAGE_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants';
import { EventFieldsData } from '../types';
import { EventFieldsData, FieldsData } from '../types';

export interface FieldValueCellProps {
contextId: string;
data: EventFieldsData;
data: EventFieldsData | FieldsData;
eventId: string;
fieldFromBrowserField?: Readonly<Record<string, Partial<BrowserField>>>;
fieldFromBrowserField?: BrowserField;
getLinkValue?: (field: string) => string | null;
linkValue?: string | null | undefined;
values: string[] | null | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export interface UseActionCellDataProvider {
eventId?: string;
field: string;
fieldFormat?: string;
fieldFromBrowserField?: Readonly<Record<string, Partial<BrowserField>>>;
fieldFromBrowserField?: BrowserField;
fieldType?: string;
isObjectArray?: boolean;
linkValue?: string | null;
Expand Down
Loading

0 comments on commit b3774e8

Please sign in to comment.