Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CTI] updates Alert Summary UI #107081

Merged
merged 13 commits into from
Aug 3, 2021
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 */}
rylnd marked this conversation as resolved.
Show resolved Hide resolved
<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';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason for preferring the FP version of this function?

Copy link
Contributor Author

@ecezalp ecezalp Aug 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no reason - updated to lodash

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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're still generating this value in buildThreatSummaryItems, but it's now unused.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the catch! removed fieldName from ThreatSummaryItem type

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}`;
rylnd marked this conversation as resolved.
Show resolved Hide resolved
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}
rylnd marked this conversation as resolved.
Show resolved Hide resolved
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intention here to give more spacing between table rows? If so, should this be applied to StyledEuiInMemoryTable so that all rows are at least a min height?

Copy link
Contributor Author

@ecezalp ecezalp Aug 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah so this is a good question - previously the table in Alert Summary tab and the table in Threat Intel tab had the same line height, and at a certain point they went out of sync. I looked through the changes and I was unable to identify how this change took place (didn't see any CSS associated with the change, and the tables are the same, with the same compressed prop that makes the rows closer to each other) - it could be that the ActionCell component is enlarging the row height in the Alert Summary tab now that I think about it. I added this explicit line height to ensure that all rows have the same line height.

`;

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',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there significance to this value? Was this specified by design, or is this intended to be more dynamic than fixed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no particular significance, it is to ensure that the indentation is fixed. Previously this value was 160px for both AlertSummary and ThreatSummary items. Recently it was changed to 33% for AlertSummary only. To ensure the consistency between AlertSummary and ThreatSummary values, a change needed to be made. Viewing the designs, I thought that a fixed 220px was good to go. We can get confirmation from @yiyangliu9286 for this one.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the design changes in 7.15, we made the "value" field indent 8px from each section header since we have added the section header in 7.15, so which setting that allows the "value" field to be indented at the right position will work here.

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