Skip to content

Commit a75a9b8

Browse files
committed
init alert details tab
1 parent 197b9dd commit a75a9b8

File tree

9 files changed

+204
-114
lines changed

9 files changed

+204
-114
lines changed

x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,24 @@
55
*/
66

77
import { EuiLink, EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui';
8-
import React, { useCallback, useMemo } from 'react';
8+
import React, { useCallback, useMemo, useState } from 'react';
99
import styled from 'styled-components';
1010

11+
import { get } from 'lodash/fp';
1112
import { BrowserFields } from '../../containers/source';
1213
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline';
1314
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
1415
import { OnUpdateColumns } from '../../../timelines/components/timeline/events';
1516
import { EventFieldsBrowser } from './event_fields_browser';
1617
import { JsonView } from './json_view';
1718
import * as i18n from './translations';
19+
import { SummaryView } from './summary_view';
1820

19-
export type View = EventsViewType.tableView | EventsViewType.jsonView;
21+
export type View = EventsViewType.tableView | EventsViewType.jsonView | EventsViewType.summaryView;
2022
export enum EventsViewType {
2123
tableView = 'table-view',
2224
jsonView = 'json-view',
25+
summaryView = 'summary-view',
2326
}
2427

2528
const CollapseLink = styled(EuiLink)`
@@ -33,9 +36,7 @@ interface Props {
3336
columnHeaders: ColumnHeaderOptions[];
3437
data: TimelineEventsDetailsItem[];
3538
id: string;
36-
view: EventsViewType;
3739
onUpdateColumns: OnUpdateColumns;
38-
onViewSelected: (selected: EventsViewType) => void;
3940
timelineId: string;
4041
toggleColumn: (column: ColumnHeaderOptions) => void;
4142
}
@@ -47,23 +48,35 @@ const Details = styled.div`
4748
Details.displayName = 'Details';
4849

4950
export const EventDetails = React.memo<Props>(
50-
({
51-
browserFields,
52-
columnHeaders,
53-
data,
54-
id,
55-
view,
56-
onUpdateColumns,
57-
onViewSelected,
58-
timelineId,
59-
toggleColumn,
60-
}) => {
61-
const handleTabClick = useCallback((e) => onViewSelected(e.id as EventsViewType), [
62-
onViewSelected,
63-
]);
51+
({ browserFields, columnHeaders, data, id, onUpdateColumns, timelineId, toggleColumn }) => {
52+
const [view, setView] = useState<View>(EventsViewType.tableView);
6453

54+
const handleTabClick = useCallback((e) => setView(e.id), [setView]);
55+
const eventKindData = useMemo(() => (data || []).find((item) => item.field === 'event.kind'), [
56+
data,
57+
]);
58+
const eventKind = get('values.0', eventKindData);
59+
const alerts = useMemo(
60+
() => [
61+
{
62+
id: EventsViewType.summaryView,
63+
name: i18n.SUMMARY,
64+
content: (
65+
<SummaryView
66+
data={data}
67+
eventId={id}
68+
browserFields={browserFields}
69+
columnHeaders={columnHeaders}
70+
timelineId={timelineId}
71+
/>
72+
),
73+
},
74+
],
75+
[data, id, browserFields, columnHeaders, timelineId]
76+
);
6577
const tabs: EuiTabbedContentTab[] = useMemo(
6678
() => [
79+
...(eventKind !== 'event' ? alerts : []),
6780
{
6881
id: EventsViewType.tableView,
6982
name: i18n.TABLE,
@@ -85,16 +98,24 @@ export const EventDetails = React.memo<Props>(
8598
content: <JsonView data={data} />,
8699
},
87100
],
88-
[browserFields, columnHeaders, data, id, onUpdateColumns, timelineId, toggleColumn]
101+
[
102+
browserFields,
103+
columnHeaders,
104+
data,
105+
id,
106+
onUpdateColumns,
107+
timelineId,
108+
toggleColumn,
109+
alerts,
110+
eventKind,
111+
]
89112
);
90113

114+
const selectedTab = useMemo(() => tabs.find((tab) => tab.id === view), [tabs, view]);
115+
91116
return (
92117
<Details data-test-subj="eventDetails">
93-
<EuiTabbedContent
94-
tabs={tabs}
95-
selectedTab={view === 'table-view' ? tabs[0] : tabs[1]}
96-
onTabClick={handleTabClick}
97-
/>
118+
<EuiTabbedContent tabs={tabs} selectedTab={selectedTab} onTabClick={handleTabClick} />
98119
</Details>
99120
);
100121
}

x-pack/plugins/security_solution/public/common/components/event_details/stateful_event_details.tsx

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
import React, { useMemo } from 'react';
7+
8+
import {
9+
EuiDescriptionList,
10+
EuiSpacer,
11+
EuiDescriptionListTitle,
12+
EuiDescriptionListDescription,
13+
} from '@elastic/eui';
14+
import { get, getOr } from 'lodash/fp';
15+
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
16+
import { OverflowField } from '../tables/helpers';
17+
import { FormattedFieldValue } from '../../../timelines/components/timeline/body/renderers/formatted_field';
18+
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
19+
import * as i18n from './translations';
20+
21+
type Summary = Array<{ title: string; description: JSX.Element }>;
22+
23+
const fields = [
24+
'@timestamp',
25+
'signal.status',
26+
'signal.rule.name',
27+
'signal.rule.severity',
28+
'signal.rule.riskScore',
29+
'user.name',
30+
'host.name',
31+
'source.ip',
32+
'destination.ip',
33+
];
34+
35+
const SummaryViewComponent: React.FC<{
36+
data: TimelineEventsDetailsItem[];
37+
eventId: string;
38+
columnHeaders: ColumnHeaderOptions[];
39+
timelineId: string;
40+
}> = ({ data, eventId, columnHeaders, timelineId }) => {
41+
const summaryList = useMemo(() => {
42+
return data.reduce<Summary>((acc, item) => {
43+
const column = columnHeaders.find((c) => c.id === item.field);
44+
const fieldValue = getOr(null, 'values.0', item);
45+
return fields.indexOf(item.field) >= 0
46+
? [
47+
...acc,
48+
{
49+
title: item.field,
50+
description: (
51+
<FormattedFieldValue
52+
contextId={`alert-details-value-formatted-field-value-${timelineId}-${eventId}-${item.field}-${fieldValue}`}
53+
eventId={eventId}
54+
fieldFormat={column?.format}
55+
fieldName={item.field}
56+
fieldType={column?.type ?? 'string'}
57+
value={fieldValue}
58+
/>
59+
),
60+
},
61+
]
62+
: acc;
63+
}, []);
64+
}, [data, columnHeaders, eventId, timelineId]);
65+
66+
const messageData = useMemo(() => (data || []).find((item) => item.field === 'message'), [data]);
67+
const message = get('values.0', messageData);
68+
69+
return (
70+
<>
71+
<EuiSpacer />
72+
<EuiDescriptionList type="responsiveColumn" listItems={summaryList} />
73+
{message && (
74+
<>
75+
<EuiSpacer />
76+
<EuiDescriptionList>
77+
<EuiDescriptionListTitle>{i18n.INVESTIGATION_GUIDE}</EuiDescriptionListTitle>
78+
<EuiDescriptionListDescription>
79+
<OverflowField value={message} />
80+
</EuiDescriptionListDescription>
81+
</EuiDescriptionList>
82+
</>
83+
)}
84+
</>
85+
);
86+
};
87+
88+
SummaryViewComponent.displayName = 'SummaryViewComponent';
89+
90+
export const SummaryView = React.memo(SummaryViewComponent);

x-pack/plugins/security_solution/public/common/components/event_details/translations.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66

77
import { i18n } from '@kbn/i18n';
88

9+
export const SUMMARY = i18n.translate('xpack.securitySolution.alertDetails.summary', {
10+
defaultMessage: 'Summary',
11+
});
12+
13+
export const INVESTIGATION_GUIDE = i18n.translate(
14+
'xpack.securitySolution.alertDetails.summary.investigationGuide',
15+
{
16+
defaultMessage: 'Investigation guide',
17+
}
18+
);
19+
920
export const TABLE = i18n.translate('xpack.securitySolution.eventDetails.table', {
1021
defaultMessage: 'Table',
1122
});

x-pack/plugins/security_solution/public/common/components/events_viewer/event_details_flyout.tsx

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader } from '@elastic/eui';
7+
import { EuiFlyout } from '@elastic/eui';
88
import React, { useCallback } from 'react';
99
import styled from 'styled-components';
1010
import deepEqual from 'fast-deep-equal';
@@ -13,10 +13,7 @@ import { useDispatch } from 'react-redux';
1313
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
1414
import { timelineActions } from '../../../timelines/store/timeline';
1515
import { BrowserFields, DocValueFields } from '../../containers/source';
16-
import {
17-
ExpandableEvent,
18-
ExpandableEventTitle,
19-
} from '../../../timelines/components/timeline/expandable_event';
16+
import { ExpandableEvent } from '../../../timelines/components/timeline/expandable_event';
2017
import { useDeepEqualSelector } from '../../hooks/use_selector';
2118

2219
const StyledEuiFlyout = styled(EuiFlyout)`
@@ -56,18 +53,13 @@ const EventDetailsFlyoutComponent: React.FC<EventDetailsFlyoutProps> = ({
5653

5754
return (
5855
<StyledEuiFlyout size="s" onClose={handleClearSelection}>
59-
<EuiFlyoutHeader hasBorder>
60-
<ExpandableEventTitle />
61-
</EuiFlyoutHeader>
62-
<EuiFlyoutBody>
63-
<ExpandableEvent
64-
browserFields={browserFields}
65-
docValueFields={docValueFields}
66-
event={expandedEvent}
67-
timelineId={timelineId}
68-
toggleColumn={toggleColumn}
69-
/>
70-
</EuiFlyoutBody>
56+
<ExpandableEvent
57+
browserFields={browserFields}
58+
docValueFields={docValueFields}
59+
event={expandedEvent}
60+
timelineId={timelineId}
61+
toggleColumn={toggleColumn}
62+
/>
7163
</StyledEuiFlyout>
7264
);
7365
};

x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ const StatefulEventComponent: React.FC<Props> = ({
112112
const handleOnEventToggled = useCallback(() => {
113113
const eventId = event._id;
114114
const indexName = event._index!;
115-
116115
dispatch(
117116
timelineActions.toggleExpandedEvent({
118117
timelineId,
@@ -127,7 +126,7 @@ const StatefulEventComponent: React.FC<Props> = ({
127126
if (timelineId === TimelineId.active) {
128127
activeTimeline.toggleExpandedEvent({ eventId, indexName, loading: false });
129128
}
130-
}, [dispatch, event._id, event._index, timelineId]);
129+
}, [dispatch, timelineId, event]);
131130

132131
const associateNote = useCallback(
133132
(noteId: string) => {

x-pack/plugins/security_solution/public/timelines/components/timeline/event_details.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,7 @@ import deepEqual from 'fast-deep-equal';
1616

1717
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
1818
import { BrowserFields, DocValueFields } from '../../../common/containers/source';
19-
import {
20-
ExpandableEvent,
21-
ExpandableEventTitle,
22-
} from '../../../timelines/components/timeline/expandable_event';
19+
import { ExpandableEvent } from '../../../timelines/components/timeline/expandable_event';
2320
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
2421

2522
interface EventDetailsProps {
@@ -41,7 +38,6 @@ const EventDetailsComponent: React.FC<EventDetailsProps> = ({
4138

4239
return (
4340
<>
44-
<ExpandableEventTitle />
4541
<EuiSpacer />
4642
<ExpandableEvent
4743
browserFields={browserFields}

0 commit comments

Comments
 (0)