Skip to content

Commit

Permalink
init alert details tab
Browse files Browse the repository at this point in the history
  • Loading branch information
angorayc committed Nov 23, 2020
1 parent 197b9dd commit a75a9b8
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@
*/

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

import { get } from 'lodash/fp';
import { BrowserFields } from '../../containers/source';
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline';
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
import { OnUpdateColumns } from '../../../timelines/components/timeline/events';
import { EventFieldsBrowser } from './event_fields_browser';
import { JsonView } from './json_view';
import * as i18n from './translations';
import { SummaryView } from './summary_view';

export type View = EventsViewType.tableView | EventsViewType.jsonView;
export type View = EventsViewType.tableView | EventsViewType.jsonView | EventsViewType.summaryView;
export enum EventsViewType {
tableView = 'table-view',
jsonView = 'json-view',
summaryView = 'summary-view',
}

const CollapseLink = styled(EuiLink)`
Expand All @@ -33,9 +36,7 @@ interface Props {
columnHeaders: ColumnHeaderOptions[];
data: TimelineEventsDetailsItem[];
id: string;
view: EventsViewType;
onUpdateColumns: OnUpdateColumns;
onViewSelected: (selected: EventsViewType) => void;
timelineId: string;
toggleColumn: (column: ColumnHeaderOptions) => void;
}
Expand All @@ -47,23 +48,35 @@ const Details = styled.div`
Details.displayName = 'Details';

export const EventDetails = React.memo<Props>(
({
browserFields,
columnHeaders,
data,
id,
view,
onUpdateColumns,
onViewSelected,
timelineId,
toggleColumn,
}) => {
const handleTabClick = useCallback((e) => onViewSelected(e.id as EventsViewType), [
onViewSelected,
]);
({ browserFields, columnHeaders, data, id, onUpdateColumns, timelineId, toggleColumn }) => {
const [view, setView] = useState<View>(EventsViewType.tableView);

const handleTabClick = useCallback((e) => setView(e.id), [setView]);
const eventKindData = useMemo(() => (data || []).find((item) => item.field === 'event.kind'), [
data,
]);
const eventKind = get('values.0', eventKindData);
const alerts = useMemo(
() => [
{
id: EventsViewType.summaryView,
name: i18n.SUMMARY,
content: (
<SummaryView
data={data}
eventId={id}
browserFields={browserFields}
columnHeaders={columnHeaders}
timelineId={timelineId}
/>
),
},
],
[data, id, browserFields, columnHeaders, timelineId]
);
const tabs: EuiTabbedContentTab[] = useMemo(
() => [
...(eventKind !== 'event' ? alerts : []),
{
id: EventsViewType.tableView,
name: i18n.TABLE,
Expand All @@ -85,16 +98,24 @@ export const EventDetails = React.memo<Props>(
content: <JsonView data={data} />,
},
],
[browserFields, columnHeaders, data, id, onUpdateColumns, timelineId, toggleColumn]
[
browserFields,
columnHeaders,
data,
id,
onUpdateColumns,
timelineId,
toggleColumn,
alerts,
eventKind,
]
);

const selectedTab = useMemo(() => tabs.find((tab) => tab.id === view), [tabs, view]);

return (
<Details data-test-subj="eventDetails">
<EuiTabbedContent
tabs={tabs}
selectedTab={view === 'table-view' ? tabs[0] : tabs[1]}
onTabClick={handleTabClick}
/>
<EuiTabbedContent tabs={tabs} selectedTab={selectedTab} onTabClick={handleTabClick} />
</Details>
);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useMemo } from 'react';

import {
EuiDescriptionList,
EuiSpacer,
EuiDescriptionListTitle,
EuiDescriptionListDescription,
} from '@elastic/eui';
import { get, getOr } from 'lodash/fp';
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
import { OverflowField } from '../tables/helpers';
import { FormattedFieldValue } from '../../../timelines/components/timeline/body/renderers/formatted_field';
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
import * as i18n from './translations';

type Summary = Array<{ title: string; description: JSX.Element }>;

const fields = [
'@timestamp',
'signal.status',
'signal.rule.name',
'signal.rule.severity',
'signal.rule.riskScore',
'user.name',
'host.name',
'source.ip',
'destination.ip',
];

const SummaryViewComponent: React.FC<{
data: TimelineEventsDetailsItem[];
eventId: string;
columnHeaders: ColumnHeaderOptions[];
timelineId: string;
}> = ({ data, eventId, columnHeaders, timelineId }) => {
const summaryList = useMemo(() => {
return data.reduce<Summary>((acc, item) => {
const column = columnHeaders.find((c) => c.id === item.field);
const fieldValue = getOr(null, 'values.0', item);
return fields.indexOf(item.field) >= 0
? [
...acc,
{
title: item.field,
description: (
<FormattedFieldValue
contextId={`alert-details-value-formatted-field-value-${timelineId}-${eventId}-${item.field}-${fieldValue}`}
eventId={eventId}
fieldFormat={column?.format}
fieldName={item.field}
fieldType={column?.type ?? 'string'}
value={fieldValue}
/>
),
},
]
: acc;
}, []);
}, [data, columnHeaders, eventId, timelineId]);

const messageData = useMemo(() => (data || []).find((item) => item.field === 'message'), [data]);
const message = get('values.0', messageData);

return (
<>
<EuiSpacer />
<EuiDescriptionList type="responsiveColumn" listItems={summaryList} />
{message && (
<>
<EuiSpacer />
<EuiDescriptionList>
<EuiDescriptionListTitle>{i18n.INVESTIGATION_GUIDE}</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<OverflowField value={message} />
</EuiDescriptionListDescription>
</EuiDescriptionList>
</>
)}
</>
);
};

SummaryViewComponent.displayName = 'SummaryViewComponent';

export const SummaryView = React.memo(SummaryViewComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

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

export const SUMMARY = i18n.translate('xpack.securitySolution.alertDetails.summary', {
defaultMessage: 'Summary',
});

export const INVESTIGATION_GUIDE = i18n.translate(
'xpack.securitySolution.alertDetails.summary.investigationGuide',
{
defaultMessage: 'Investigation guide',
}
);

export const TABLE = i18n.translate('xpack.securitySolution.eventDetails.table', {
defaultMessage: 'Table',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader } from '@elastic/eui';
import { EuiFlyout } from '@elastic/eui';
import React, { useCallback } from 'react';
import styled from 'styled-components';
import deepEqual from 'fast-deep-equal';
Expand All @@ -13,10 +13,7 @@ import { useDispatch } from 'react-redux';
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
import { timelineActions } from '../../../timelines/store/timeline';
import { BrowserFields, DocValueFields } from '../../containers/source';
import {
ExpandableEvent,
ExpandableEventTitle,
} from '../../../timelines/components/timeline/expandable_event';
import { ExpandableEvent } from '../../../timelines/components/timeline/expandable_event';
import { useDeepEqualSelector } from '../../hooks/use_selector';

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

return (
<StyledEuiFlyout size="s" onClose={handleClearSelection}>
<EuiFlyoutHeader hasBorder>
<ExpandableEventTitle />
</EuiFlyoutHeader>
<EuiFlyoutBody>
<ExpandableEvent
browserFields={browserFields}
docValueFields={docValueFields}
event={expandedEvent}
timelineId={timelineId}
toggleColumn={toggleColumn}
/>
</EuiFlyoutBody>
<ExpandableEvent
browserFields={browserFields}
docValueFields={docValueFields}
event={expandedEvent}
timelineId={timelineId}
toggleColumn={toggleColumn}
/>
</StyledEuiFlyout>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ const StatefulEventComponent: React.FC<Props> = ({
const handleOnEventToggled = useCallback(() => {
const eventId = event._id;
const indexName = event._index!;

dispatch(
timelineActions.toggleExpandedEvent({
timelineId,
Expand All @@ -127,7 +126,7 @@ const StatefulEventComponent: React.FC<Props> = ({
if (timelineId === TimelineId.active) {
activeTimeline.toggleExpandedEvent({ eventId, indexName, loading: false });
}
}, [dispatch, event._id, event._index, timelineId]);
}, [dispatch, timelineId, event]);

const associateNote = useCallback(
(noteId: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ import deepEqual from 'fast-deep-equal';

import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
import { BrowserFields, DocValueFields } from '../../../common/containers/source';
import {
ExpandableEvent,
ExpandableEventTitle,
} from '../../../timelines/components/timeline/expandable_event';
import { ExpandableEvent } from '../../../timelines/components/timeline/expandable_event';
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';

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

return (
<>
<ExpandableEventTitle />
<EuiSpacer />
<ExpandableEvent
browserFields={browserFields}
Expand Down
Loading

0 comments on commit a75a9b8

Please sign in to comment.