Skip to content

Commit

Permalink
[SIEM] Adds Signals Table and additional configuration options to Sta…
Browse files Browse the repository at this point in the history
…tefulEventsViewer (elastic#52044)

## Summary

This is `Part I` of `II` for adding the `Signals Table` to the main Detection Engine landing page ([meta issue](elastic#50405)). Breaking into two parts as this contains additional configuration options to the `StatefulEventsViewer` which will be used as part of elastic#51016.

`Part I` includes:
* `SignalsTable` component that displays signals from the default signals index `.siem-signals`
* Refactors `StatefulEventsViewer` to use `useFetchIndexPatterns` hook instead of `WithSource`
* Adds ability to specify `alias` to `ColumnHeader` when providing column names
* Adds the following new props to `StatefulEventsViewer`
  * `defaultIndices?: string[]` -- for specifying a different index than `siemDefaultIndex`
  * `headerFilterGroup?: React.ReactNode` -- for providing a component to display in the top right of the table (e.g. filter buttons, select, etc.)
  * `timelineTypeContext?: TimelineTypeContextProps` -- config for when creating a new table
    * `documentType?: string` -- user string for type of records displayed (e.g. Signals)
    * `footerText?: string` -- custom footer text for given document type
    * `showCheckboxes: boolean` -- whether or not to show selection checkboxes
    * `showRowRenderers: boolean` -- whether or not to show row renderers
    * `timelineType: TimelineType` -- type of Timeline for setting default columns
    * `title?: string` -- optional custom title
  * `utilityBar?: (totalCount: number) => React.ReactNode` -- optional param for providing your own custom `UtilityBar` instead of using the default `Showing xxx events`.

`Part II` will add support for selection and overflow/batch actions.

<img width="1548" alt="Screen Shot 2019-12-02 at 19 59 34" src="https://user-images.githubusercontent.com/2946766/70016801-89aa0c80-153e-11ea-9dbf-b7b8648fb260.png">

### Checklist

Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR.

- [x] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)
- [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)
  * Note: some placeholders were moved to their own files, and so some raw strings will still exist
- [ ] ~[Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~
- [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios
- [ ] ~This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~

### For maintainers

- [ ] ~This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~
- [ ] ~This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~
  • Loading branch information
spong committed Dec 5, 2019
1 parent 6cfa632 commit fdbbb61
Show file tree
Hide file tree
Showing 34 changed files with 765 additions and 348 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 { defaultHeaders } from './default_headers';
import { SubsetTimelineModel, timelineDefaults } from '../../store/timeline/model';

export const eventsDefaultModel: SubsetTimelineModel = {
...timelineDefaults,
columns: defaultHeaders,
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import { mount } from 'enzyme';
import React from 'react';
import { MockedProvider } from 'react-apollo/test-utils';

import { TestProviders } from '../../mock';
import { mockIndexPattern, TestProviders } from '../../mock';
import { mockUiSettings } from '../../mock/ui_settings';
import { wait } from '../../lib/helpers';

import { mockEventViewerResponse } from './mock';
import { StatefulEventsViewer } from '.';
import { defaultHeaders } from './default_headers';
import { useKibanaCore } from '../../lib/compose/kibana_core';
import { useFetchIndexPatterns } from '../../containers/detection_engine/rules/fetch_index_patterns';
import { mockBrowserFields } from '../../containers/source/mock';
import { eventsDefaultModel } from './default_model';

jest.mock('../../lib/settings/use_kibana_ui_setting');

Expand All @@ -25,6 +28,15 @@ mockUseKibanaCore.mockImplementation(() => ({
uiSettings: mockUiSettings,
}));

const mockUseFetchIndexPatterns: jest.Mock = useFetchIndexPatterns as jest.Mock;
jest.mock('../../containers/detection_engine/rules/fetch_index_patterns');
mockUseFetchIndexPatterns.mockImplementation(() => [
{
browserFields: mockBrowserFields,
indexPatterns: mockIndexPattern,
},
]);

const from = 1566943856794;
const to = 1566857456791;

Expand All @@ -33,7 +45,12 @@ describe('EventsViewer', () => {
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<StatefulEventsViewer end={to} id={'test-stateful-events-viewer'} start={from} />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}
end={to}
id={'test-stateful-events-viewer'}
start={from}
/>
</MockedProvider>
</TestProviders>
);
Expand All @@ -53,7 +70,12 @@ describe('EventsViewer', () => {
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<StatefulEventsViewer end={to} id={'test-stateful-events-viewer'} start={from} />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}
end={to}
id={'test-stateful-events-viewer'}
start={from}
/>
</MockedProvider>
</TestProviders>
);
Expand All @@ -73,7 +95,12 @@ describe('EventsViewer', () => {
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<StatefulEventsViewer end={to} id={'test-stateful-events-viewer'} start={from} />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}
end={to}
id={'test-stateful-events-viewer'}
start={from}
/>
</MockedProvider>
</TestProviders>
);
Expand All @@ -94,7 +121,12 @@ describe('EventsViewer', () => {
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<StatefulEventsViewer end={to} id={'test-stateful-events-viewer'} start={from} />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}
end={to}
id={'test-stateful-events-viewer'}
start={from}
/>
</MockedProvider>
</TestProviders>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ import { Footer, footerHeight } from '../timeline/footer';
import { combineQueries } from '../timeline/helpers';
import { TimelineRefetch } from '../timeline/refetch_timeline';
import { isCompactFooter } from '../timeline/timeline';
import { ManageTimelineContext } from '../timeline/timeline_context';
import { ManageTimelineContext, TimelineTypeContextProps } from '../timeline/timeline_context';
import * as i18n from './translations';
import {
IIndexPattern,
Query,
esFilters,
esQuery,
IIndexPattern,
Query,
} from '../../../../../../../src/plugins/data/public';

const DEFAULT_EVENTS_VIEWER_HEIGHT = 500;
Expand All @@ -48,6 +48,7 @@ interface Props {
dataProviders: DataProvider[];
end: number;
filters: esFilters.Filter[];
headerFilterGroup?: React.ReactNode;
height?: number;
id: string;
indexPattern: IIndexPattern;
Expand All @@ -60,7 +61,9 @@ interface Props {
showInspect: boolean;
start: number;
sort: Sort;
timelineTypeContext: TimelineTypeContextProps;
toggleColumn: (column: ColumnHeader) => void;
utilityBar?: (totalCount: number) => React.ReactNode;
}

export const EventsViewer = React.memo<Props>(
Expand All @@ -70,6 +73,7 @@ export const EventsViewer = React.memo<Props>(
dataProviders,
end,
filters,
headerFilterGroup,
height = DEFAULT_EVENTS_VIEWER_HEIGHT,
id,
indexPattern,
Expand All @@ -82,7 +86,9 @@ export const EventsViewer = React.memo<Props>(
showInspect,
start,
sort,
timelineTypeContext,
toggleColumn,
utilityBar,
}) => {
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
const core = useKibanaCore();
Expand Down Expand Up @@ -116,6 +122,7 @@ export const EventsViewer = React.memo<Props>(
fields={columnsHeader.map(c => c.id)}
filterQuery={combinedQueries.filterQuery}
id={id}
indexPattern={indexPattern}
limit={itemsPerPage}
sortField={{
sortFieldId: sort.columnId,
Expand All @@ -137,17 +144,29 @@ export const EventsViewer = React.memo<Props>(
<HeaderSection
id={id}
showInspect={showInspect}
subtitle={`${i18n.SHOWING}: ${totalCount.toLocaleString()} ${i18n.UNIT(
totalCount
)}`}
title={i18n.EVENTS}
/>
subtitle={
utilityBar
? undefined
: `${i18n.SHOWING}: ${totalCount.toLocaleString()} ${i18n.UNIT(
totalCount
)}`
}
title={timelineTypeContext?.title ?? i18n.EVENTS}
>
{headerFilterGroup}
</HeaderSection>

{utilityBar?.(totalCount)}

<div
data-test-subj={`events-container-loading-${loading}`}
style={{ width: `${width}px` }}
>
<ManageTimelineContext loading={loading} width={width}>
<ManageTimelineContext
loading={loading}
width={width}
type={timelineTypeContext}
>
<TimelineRefetch
id={id}
inputId="global"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import { MockedProvider } from 'react-apollo/test-utils';

import { useKibanaCore } from '../../lib/compose/kibana_core';
import { wait } from '../../lib/helpers';
import { TestProviders } from '../../mock';
import { mockIndexPattern, TestProviders } from '../../mock';
import { mockUiSettings } from '../../mock/ui_settings';

import { mockEventViewerResponse } from './mock';
import { StatefulEventsViewer } from '.';
import { useFetchIndexPatterns } from '../../containers/detection_engine/rules/fetch_index_patterns';
import { mockBrowserFields } from '../../containers/source/mock';
import { eventsDefaultModel } from './default_model';

jest.mock('../../lib/settings/use_kibana_ui_setting');

Expand All @@ -24,6 +27,15 @@ mockUseKibanaCore.mockImplementation(() => ({
uiSettings: mockUiSettings,
}));

const mockUseFetchIndexPatterns: jest.Mock = useFetchIndexPatterns as jest.Mock;
jest.mock('../../containers/detection_engine/rules/fetch_index_patterns');
mockUseFetchIndexPatterns.mockImplementation(() => [
{
browserFields: mockBrowserFields,
indexPatterns: mockIndexPattern,
},
]);

const from = 1566943856794;
const to = 1566857456791;

Expand All @@ -32,7 +44,12 @@ describe('StatefulEventsViewer', () => {
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<StatefulEventsViewer end={to} id={'test-stateful-events-viewer'} start={from} />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}
end={to}
id={'test-stateful-events-viewer'}
start={from}
/>
</MockedProvider>
</TestProviders>
);
Expand All @@ -52,7 +69,12 @@ describe('StatefulEventsViewer', () => {
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<StatefulEventsViewer end={to} id={'test-stateful-events-viewer'} start={from} />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}
end={to}
id={'test-stateful-events-viewer'}
start={from}
/>
</MockedProvider>
</TestProviders>
);
Expand All @@ -72,7 +94,12 @@ describe('StatefulEventsViewer', () => {
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockEventViewerResponse} addTypename={false}>
<StatefulEventsViewer end={to} id={'test-stateful-events-viewer'} start={from} />
<StatefulEventsViewer
defaultModel={eventsDefaultModel}
end={to}
id={'test-stateful-events-viewer'}
start={from}
/>
</MockedProvider>
</TestProviders>
);
Expand Down
Loading

0 comments on commit fdbbb61

Please sign in to comment.