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

[SIEM] Create template timeline #63136

Merged
merged 52 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
d3b3ba6
init routes for template timeline
angorayc Apr 8, 2020
8a05dd9
create template timeline
angorayc Apr 9, 2020
79d53c8
Merge remote-tracking branch 'upstream/master' into template-timeline
angorayc Apr 9, 2020
99f0da5
add create/update timelines route
angorayc Apr 15, 2020
5a9b6ac
update api entry point
angorayc Apr 15, 2020
91798ee
fix types
angorayc Apr 15, 2020
dcae730
add template type
angorayc Apr 15, 2020
086996a
Merge remote-tracking branch 'upstream/master' into template-timeline
angorayc Apr 16, 2020
cab3e60
fix types
angorayc Apr 16, 2020
a33cbb6
add types and template timeline id
angorayc Apr 16, 2020
8243614
fix types
angorayc Apr 16, 2020
89f2b12
update import timeline to handle template timeline
angorayc Apr 16, 2020
464ee77
unit test
angorayc Apr 17, 2020
3a37628
sudo code
XavierM Apr 17, 2020
9bb3552
remove class for savedobject
angorayc Apr 20, 2020
aba9d8e
add template timeline version
angorayc Apr 20, 2020
e26a803
clean up arguments
angorayc Apr 20, 2020
5351219
fix types for framework request
XavierM Apr 20, 2020
7584715
show filter in find
XavierM Apr 20, 2020
d120d73
fix create template timeline
angorayc Apr 20, 2020
7aeb123
update mock data
angorayc Apr 20, 2020
7416fca
handle missing timeline when exporting
angorayc Apr 21, 2020
4b93431
update the order for timeline routes
angorayc Apr 21, 2020
3c51485
update schemas
angorayc Apr 21, 2020
c17ea0c
Merge remote-tracking branch 'upstream/master' into template-timeline
angorayc Apr 21, 2020
b94159f
Merge remote-tracking branch 'upstream/master' into template-timeline
angorayc Apr 21, 2020
0a7b275
move type to common folder so we can re-use them on UI and server side
XavierM Apr 21, 2020
0f22a03
fix types + integrate persist with epic timeline
XavierM Apr 21, 2020
bebb133
update all timeline when persit timeline
XavierM Apr 21, 2020
785ec34
add timeline api readme
angorayc Apr 22, 2020
2abe09b
fix validation error
angorayc Apr 22, 2020
8707b34
fix unit test
angorayc Apr 22, 2020
b7fa77a
Merge remote-tracking branch 'upstream/master' into template-timeline
angorayc Apr 23, 2020
8c56978
Merge branch 'master' into template-timeline
elasticmachine Apr 24, 2020
be2aea3
display error if unexpected format is given
angorayc Apr 27, 2020
7194dc5
Merge branch 'template-timeline' of github.com:angorayc/kibana into t…
angorayc Apr 27, 2020
21b535e
Merge remote-tracking branch 'upstream/master' into template-timeline
angorayc Apr 27, 2020
292c35e
Merge branch 'master' into template-timeline
elasticmachine Apr 27, 2020
f106ff4
fix issue with reftech all timeline query
XavierM Apr 27, 2020
a8c0501
fix flashing timeline while refetch
angorayc Apr 28, 2020
d1c851e
Merge branch 'template-timeline' of github.com:angorayc/kibana into t…
angorayc Apr 28, 2020
4eca630
fix types
angorayc Apr 28, 2020
7d3d120
Merge remote-tracking branch 'upstream/master' into template-timeline
angorayc Apr 28, 2020
5315cc7
fix types
angorayc Apr 28, 2020
1995a71
fix dependency
angorayc Apr 28, 2020
6413a11
fix timeline deletion
XavierM Apr 28, 2020
b1d54e7
Merge branch 'template-timeline' of github.com:angorayc/kibana into t…
XavierM Apr 28, 2020
48d43b7
remove redundant dependency
angorayc Apr 28, 2020
3993d5d
add i18n message
angorayc Apr 28, 2020
184711f
Merge branch 'master' into template-timeline
elasticmachine Apr 28, 2020
fd3a036
Merge branch 'master' into template-timeline
elasticmachine Apr 28, 2020
bdade71
fix unit test
angorayc Apr 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { DeleteTimelines } from '../types';

import { TimelineDownloader } from './export_timeline';
import { DeleteTimelineModalOverlay } from '../delete_timeline_modal';
import { exportSelectedTimeline } from '../../../containers/timeline/all/api';
import { exportSelectedTimeline } from '../../../containers/timeline/api';

export interface ExportTimeline {
disableExportTimelineDownloader: () => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,41 @@ import { mount } from 'enzyme';
import { MockedProvider } from 'react-apollo/test-utils';
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { getOr } from 'lodash/fp';

import { wait } from '../../lib/helpers';
import { TestProviderWithoutDragAndDrop, apolloClient } from '../../mock/test_providers';
import { mockOpenTimelineQueryResults } from '../../mock/timeline_results';
import { DEFAULT_SEARCH_RESULTS_PER_PAGE } from '../../pages/timelines/timelines_page';

import { StatefulOpenTimeline } from '.';
import { NotePreviews } from './note_previews';
import { OPEN_TIMELINE_CLASS_NAME } from './helpers';

import { StatefulOpenTimeline } from '.';
import { useGetAllTimeline, getAllTimeline } from '../../containers/timeline/all';
jest.mock('../../lib/kibana');
jest.mock('../../containers/timeline/all', () => {
const originalModule = jest.requireActual('../../containers/timeline/all');
return {
useGetAllTimeline: jest.fn(),
getAllTimeline: originalModule.getAllTimeline,
};
});

describe('StatefulOpenTimeline', () => {
const theme = () => ({ eui: euiDarkVars, darkMode: true });
const title = 'All Timelines / Open Timelines';
beforeEach(() => {
((useGetAllTimeline as unknown) as jest.Mock).mockReturnValue({
fetchAllTimeline: jest.fn(),
timelines: getAllTimeline(
'',
getOr([], 'getAllTimeline.timeline', mockOpenTimelineQueryResults[0].result.data)
angorayc marked this conversation as resolved.
Show resolved Hide resolved
),
loading: false,
totalCount: mockOpenTimelineQueryResults[0].result.data.getAllTimeline.totalCount,
refetch: jest.fn(),
});
});

test('it has the expected initial state', () => {
const wrapper = mount(
Expand Down Expand Up @@ -459,6 +479,8 @@ describe('StatefulOpenTimeline', () => {
.find('[data-test-subj="expand-notes"]')
.first()
.simulate('click');
expect(wrapper.find('[data-test-subj="note-previews-container"]').exists()).toEqual(true);
expect(wrapper.find('[data-test-subj="updated-by"]').exists()).toEqual(true);

expect(
wrapper
Expand Down Expand Up @@ -532,7 +554,7 @@ describe('StatefulOpenTimeline', () => {
test('it renders the expected count of matching timelines when no query has been entered', async () => {
const wrapper = mount(
<ThemeProvider theme={theme}>
<MockedProvider mocks={mockOpenTimelineQueryResults} addTypename={false}>
<MockedProvider addTypename={false}>
<TestProviderWithoutDragAndDrop>
<StatefulOpenTimeline
data-test-subj="stateful-timeline"
Expand Down
137 changes: 68 additions & 69 deletions x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { connect, ConnectedProps } from 'react-redux';
import { Dispatch } from 'redux';
import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers';
import { deleteTimelineMutation } from '../../containers/timeline/delete/persist.gql_query';
import { AllTimelinesVariables, AllTimelinesQuery } from '../../containers/timeline/all';
import { AllTimelinesVariables, useGetAllTimeline } from '../../containers/timeline/all';
import { allTimelinesQuery } from '../../containers/timeline/all/index.gql_query';
import { DeleteTimelineMutation, SortFieldTimeline, Direction } from '../../graphql/types';
import { State, timelineSelectors } from '../../store';
Expand Down Expand Up @@ -104,6 +104,8 @@ export const StatefulOpenTimelineComponent = React.memo<OpenTimelineOwnProps>(
/** The requested field to sort on */
const [sortField, setSortField] = useState(DEFAULT_SORT_FIELD);

const { fetchAllTimeline, timelines, loading, totalCount, refetch } = useGetAllTimeline();

/** Invoked when the user presses enters to submit the text in the search input */
const onQueryChange: OnQueryChange = useCallback((query: EuiSearchBarQuery) => {
setSearch(query.queryText.trim());
Expand Down Expand Up @@ -251,75 +253,73 @@ export const StatefulOpenTimelineComponent = React.memo<OpenTimelineOwnProps>(
focusInput();
}, []);

return (
<AllTimelinesQuery
pageInfo={{
useEffect(() => {
fetchAllTimeline({
pageInfo: {
pageIndex: pageIndex + 1,
pageSize,
}}
search={search}
sort={{ sortField: sortField as SortFieldTimeline, sortOrder: sortDirection as Direction }}
onlyUserFavorite={onlyFavorites}
>
{({ timelines, loading, totalCount, refetch }) => {
return !isModal ? (
<OpenTimeline
data-test-subj={'open-timeline'}
deleteTimelines={onDeleteOneTimeline}
defaultPageSize={defaultPageSize}
isLoading={loading}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
importDataModalToggle={importDataModalToggle}
onAddTimelinesToFavorites={undefined}
onDeleteSelected={onDeleteSelected}
onlyFavorites={onlyFavorites}
onOpenTimeline={openTimeline}
onQueryChange={onQueryChange}
onSelectionChange={onSelectionChange}
onTableChange={onTableChange}
onToggleOnlyFavorites={onToggleOnlyFavorites}
onToggleShowNotes={onToggleShowNotes}
pageIndex={pageIndex}
pageSize={pageSize}
query={search}
refetch={refetch}
searchResults={timelines}
setImportDataModalToggle={setImportDataModalToggle}
selectedItems={selectedItems}
sortDirection={sortDirection}
sortField={sortField}
title={title}
totalSearchResultsCount={totalCount}
/>
) : (
<OpenTimelineModalBody
data-test-subj={'open-timeline-modal'}
deleteTimelines={onDeleteOneTimeline}
defaultPageSize={defaultPageSize}
hideActions={hideActions}
isLoading={loading}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
onAddTimelinesToFavorites={undefined}
onlyFavorites={onlyFavorites}
onOpenTimeline={openTimeline}
onQueryChange={onQueryChange}
onSelectionChange={onSelectionChange}
onTableChange={onTableChange}
onToggleOnlyFavorites={onToggleOnlyFavorites}
onToggleShowNotes={onToggleShowNotes}
pageIndex={pageIndex}
pageSize={pageSize}
query={search}
searchResults={timelines}
selectedItems={selectedItems}
sortDirection={sortDirection}
sortField={sortField}
title={title}
totalSearchResultsCount={totalCount}
/>
);
}}
</AllTimelinesQuery>
},
search,
sort: { sortField: sortField as SortFieldTimeline, sortOrder: sortDirection as Direction },
onlyUserFavorite: onlyFavorites,
});
}, [pageIndex, pageSize, search, sortField, sortDirection, onlyFavorites]);

return !isModal ? (
<OpenTimeline
data-test-subj={'open-timeline'}
deleteTimelines={onDeleteOneTimeline}
defaultPageSize={defaultPageSize}
isLoading={loading}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
importDataModalToggle={importDataModalToggle}
onAddTimelinesToFavorites={undefined}
onDeleteSelected={onDeleteSelected}
onlyFavorites={onlyFavorites}
onOpenTimeline={openTimeline}
onQueryChange={onQueryChange}
onSelectionChange={onSelectionChange}
onTableChange={onTableChange}
onToggleOnlyFavorites={onToggleOnlyFavorites}
onToggleShowNotes={onToggleShowNotes}
pageIndex={pageIndex}
pageSize={pageSize}
query={search}
refetch={refetch}
searchResults={timelines}
setImportDataModalToggle={setImportDataModalToggle}
selectedItems={selectedItems}
sortDirection={sortDirection}
sortField={sortField}
title={title}
totalSearchResultsCount={totalCount}
/>
) : (
<OpenTimelineModalBody
data-test-subj={'open-timeline-modal'}
deleteTimelines={onDeleteOneTimeline}
defaultPageSize={defaultPageSize}
hideActions={hideActions}
isLoading={loading}
itemIdToExpandedNotesRowMap={itemIdToExpandedNotesRowMap}
onAddTimelinesToFavorites={undefined}
onlyFavorites={onlyFavorites}
onOpenTimeline={openTimeline}
onQueryChange={onQueryChange}
onSelectionChange={onSelectionChange}
onTableChange={onTableChange}
onToggleOnlyFavorites={onToggleOnlyFavorites}
onToggleShowNotes={onToggleShowNotes}
pageIndex={pageIndex}
pageSize={pageSize}
query={search}
searchResults={timelines}
selectedItems={selectedItems}
sortDirection={sortDirection}
sortField={sortField}
title={title}
totalSearchResultsCount={totalCount}
/>
);
}
);
Expand All @@ -328,7 +328,6 @@ const makeMapStateToProps = () => {
const getTimeline = timelineSelectors.getTimelineByIdSelector();
const mapStateToProps = (state: State) => {
const timeline = getTimeline(state, 'timeline-1') ?? timelineDefaults;

return {
timeline,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TimelinesTable } from './timelines_table';
import { TitleRow } from './title_row';
import { ImportDataModal } from '../import_data_modal';
import * as i18n from './translations';
import { importTimelines } from '../../containers/timeline/all/api';
import { importTimelines } from '../../containers/timeline/api';

import {
UtilityBarGroup,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export interface TimelineActionsOverflowColumns {
} | null>;
}

enum TimelineTypes {
angorayc marked this conversation as resolved.
Show resolved Hide resolved
default = 'default',
template = 'template',
}
/** The results of the query run by the OpenTimeline component */
export interface OpenTimelineResult {
angorayc marked this conversation as resolved.
Show resolved Hide resolved
created?: number | null;
Expand All @@ -47,6 +51,8 @@ export interface OpenTimelineResult {
pinnedEventIds?: Readonly<Record<string, boolean>> | null;
savedObjectId?: string | null;
title?: string | null;
templateTimelineId?: string | null;
type?: TimelineTypes.template | TimelineTypes.default;
updated?: number | null;
updatedBy?: string | null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

import ApolloClient from 'apollo-client';
import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useMemo, useEffect } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { Dispatch } from 'redux';

import { AllTimelinesQuery } from '../../containers/timeline/all';
import { useGetAllTimeline } from '../../containers/timeline/all';
import { SortFieldTimeline, Direction } from '../../graphql/types';
import { queryTimelineById, dispatchUpdateTimeline } from '../open_timeline/helpers';
import { OnOpenTimeline } from '../open_timeline/types';
Expand Down Expand Up @@ -62,35 +62,37 @@ const StatefulRecentTimelinesComponent = React.memo<Props>(
[filterBy]
);

return (
<AllTimelinesQuery
pageInfo={{
const { fetchAllTimeline, timelines, loading } = useGetAllTimeline();

useEffect(() => {
fetchAllTimeline({
pageInfo: {
pageIndex: 1,
pageSize: PAGE_SIZE,
}}
search={''}
sort={{
},
search: '',
sort: {
sortField: SortFieldTimeline.updated,
sortOrder: Direction.desc,
}}
onlyUserFavorite={filterBy === 'favorites'}
>
{({ timelines, loading }) => (
<>
{loading ? (
loadingPlaceholders
) : (
<RecentTimelines
noTimelinesMessage={noTimelinesMessage}
onOpenTimeline={onOpenTimeline}
timelines={timelines}
/>
)}
<EuiHorizontalRule margin="s" />
<EuiText size="xs">{linkAllTimelines}</EuiText>
</>
},
onlyUserFavorite: filterBy === 'favorites',
});
}, [filterBy]);

return (
<>
{loading ? (
loadingPlaceholders
) : (
<RecentTimelines
noTimelinesMessage={noTimelinesMessage}
onOpenTimeline={onOpenTimeline}
timelines={timelines}
/>
)}
</AllTimelinesQuery>
<EuiHorizontalRule margin="s" />
<EuiText size="xs">{linkAllTimelines}</EuiText>
</>
);
}
);
Expand Down
Loading