From 5b02a6795191a934aa7993d9a287036b839c468d Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 May 2020 11:36:33 +0300 Subject: [PATCH 01/42] Init localStorage epic --- .../public/common/store/epic.ts | 4 +- .../public/timelines/store/timeline/epic.ts | 2 +- .../store/timeline/epic_local_storage.ts | 73 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts diff --git a/x-pack/plugins/security_solution/public/common/store/epic.ts b/x-pack/plugins/security_solution/public/common/store/epic.ts index b9e8e7d88c202a..d9de7951a86f4e 100644 --- a/x-pack/plugins/security_solution/public/common/store/epic.ts +++ b/x-pack/plugins/security_solution/public/common/store/epic.ts @@ -9,11 +9,13 @@ import { createTimelineEpic } from '../../timelines/store/timeline/epic'; import { createTimelineFavoriteEpic } from '../../timelines/store/timeline/epic_favorite'; import { createTimelineNoteEpic } from '../../timelines/store/timeline/epic_note'; import { createTimelinePinnedEventEpic } from '../../timelines/store/timeline/epic_pinned_event'; +import { createTimelineLocalStorageEpic } from '../../timelines/store/timeline/epic_local_storage'; export const createRootEpic = () => combineEpics( createTimelineEpic(), createTimelineFavoriteEpic(), createTimelineNoteEpic(), - createTimelinePinnedEventEpic() + createTimelinePinnedEventEpic(), + createTimelineLocalStorageEpic() ); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts index 5a7e5e078c7994..986c65d7a93a31 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts @@ -84,7 +84,7 @@ import { ActionTimeline, TimelineById } from './types'; import { persistTimeline } from '../../containers/api'; import { ALL_TIMELINE_QUERY_ID } from '../../containers/all'; -interface TimelineEpicDependencies { +export interface TimelineEpicDependencies { timelineByIdSelector: (state: State) => TimelineById; timelineTimeRangeSelector: (state: State) => inputsModel.TimeRange; selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery; diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts new file mode 100644 index 00000000000000..1651f437d41214 --- /dev/null +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -0,0 +1,73 @@ +/* + * 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 { Action } from 'redux'; +import { map, filter, ignoreElements, tap, merge, withLatestFrom } from 'rxjs/operators'; +import { Epic, ofType } from 'redux-observable'; +import { get } from 'lodash/fp'; + +import { createTimeline, updateTimeline } from './actions'; +import { TimelineEpicDependencies } from './epic'; +import { isNotNull } from './helpers'; +import { TimelineModel } from './model'; + +const isPageTimeline = (timelineId: string | undefined) => + timelineId && timelineId.toLowerCase() === 'alerts-table'; + +export const createTimelineLocalStorageEpic = (): Epic< + Action, + Action, + State, + TimelineEpicDependencies +> => ( + action$, + state$, + { + selectAllTimelineQuery, + selectNotesByIdSelector, + timelineByIdSelector, + timelineTimeRangeSelector, + apolloClient$, + } +) => { + const timeline$ = state$.pipe(map(timelineByIdSelector), filter(isNotNull)); + + return action$.pipe( + ofType(createTimeline.type), + withLatestFrom(timeline$), + filter(([action, timeline]) => { + const timelineId: string = get('payload.id', action); + + if (isPageTimeline(timelineId)) { + return true; + } + + return false; + }), + tap(([action, timeline]) => { + const storageTimelines = localStorage.getItem('timelines'); + const timelineId: string = get('payload.id', action); + + if (!storageTimelines) { + localStorage.setItem( + 'timelines', + JSON.stringify({ + [timelineId]: action.payload, + }) + ); + } + }), + map(([action, timeline]) => { + const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); + const timelineId: string = get('payload.id', action); + + return updateTimeline({ + id: timelineId, + timeline: { ...timeline[timelineId], ...storageTimelines[timelineId] }, + }); + }) + ); +}; From 48508fd50a46de02e77c00b78424cc37121d4e43 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 May 2020 12:27:53 +0300 Subject: [PATCH 02/42] Persist removed columns --- .../store/timeline/epic_local_storage.ts | 84 ++++++++++++------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 1651f437d41214..36496c8c35e5bb 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -5,18 +5,23 @@ */ import { Action } from 'redux'; -import { map, filter, ignoreElements, tap, merge, withLatestFrom } from 'rxjs/operators'; +import { merge } from 'rxjs'; +import { map, filter, ignoreElements, tap, withLatestFrom } from 'rxjs/operators'; import { Epic, ofType } from 'redux-observable'; import { get } from 'lodash/fp'; -import { createTimeline, updateTimeline } from './actions'; +import { createTimeline, updateTimeline, removeColumn } from './actions'; import { TimelineEpicDependencies } from './epic'; import { isNotNull } from './helpers'; -import { TimelineModel } from './model'; +import { TimelineModel, ColumnHeaderOptions } from './model'; const isPageTimeline = (timelineId: string | undefined) => timelineId && timelineId.toLowerCase() === 'alerts-table'; +const removeColumnFromTimeline = (timeline: TimelineModel, columnId: string): TimelineModel => { + return { ...timeline, columns: timeline.columns.filter(column => column.id !== columnId) }; +}; + export const createTimelineLocalStorageEpic = (): Epic< Action, Action, @@ -35,39 +40,56 @@ export const createTimelineLocalStorageEpic = (): Epic< ) => { const timeline$ = state$.pipe(map(timelineByIdSelector), filter(isNotNull)); - return action$.pipe( - ofType(createTimeline.type), - withLatestFrom(timeline$), - filter(([action, timeline]) => { - const timelineId: string = get('payload.id', action); + return merge( + action$.pipe( + ofType(createTimeline.type), + withLatestFrom(timeline$), + filter(([action, timeline]) => { + const timelineId: string = get('payload.id', action); + + if (isPageTimeline(timelineId)) { + return true; + } - if (isPageTimeline(timelineId)) { - return true; - } + return false; + }), + tap(([action, timeline]) => { + const storageTimelines = localStorage.getItem('timelines'); + const timelineId: string = get('payload.id', action); - return false; - }), - tap(([action, timeline]) => { - const storageTimelines = localStorage.getItem('timelines'); - const timelineId: string = get('payload.id', action); + if (!storageTimelines) { + localStorage.setItem( + 'timelines', + JSON.stringify({ + [timelineId]: action.payload, + }) + ); + } + }), + map(([action, timeline]) => { + const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); + const timelineId: string = get('payload.id', action); + + return updateTimeline({ + id: timelineId, + timeline: { ...timeline[timelineId], ...storageTimelines[timelineId] }, + }); + }) + ), + action$.pipe( + ofType(removeColumn.type), + tap(action => { + const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); + const timelineId: string = get('payload.id', action); + const columnId: string = get('payload.columnId', action); + const modifiedTimeline = removeColumnFromTimeline(storageTimelines[timelineId], columnId); - if (!storageTimelines) { localStorage.setItem( 'timelines', - JSON.stringify({ - [timelineId]: action.payload, - }) + JSON.stringify({ ...storageTimelines, [timelineId]: modifiedTimeline }) ); - } - }), - map(([action, timeline]) => { - const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); - const timelineId: string = get('payload.id', action); - - return updateTimeline({ - id: timelineId, - timeline: { ...timeline[timelineId], ...storageTimelines[timelineId] }, - }); - }) + }), + ignoreElements() + ) ); }; From 3c9d54458277539627e9ead67e7c47d51fa9e4fd Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 May 2020 13:07:36 +0300 Subject: [PATCH 03/42] Persist added columns --- .../store/timeline/epic_local_storage.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 36496c8c35e5bb..4271273622cd4b 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -10,7 +10,7 @@ import { map, filter, ignoreElements, tap, withLatestFrom } from 'rxjs/operators import { Epic, ofType } from 'redux-observable'; import { get } from 'lodash/fp'; -import { createTimeline, updateTimeline, removeColumn } from './actions'; +import { createTimeline, updateTimeline, removeColumn, upsertColumn } from './actions'; import { TimelineEpicDependencies } from './epic'; import { isNotNull } from './helpers'; import { TimelineModel, ColumnHeaderOptions } from './model'; @@ -22,6 +22,13 @@ const removeColumnFromTimeline = (timeline: TimelineModel, columnId: string): Ti return { ...timeline, columns: timeline.columns.filter(column => column.id !== columnId) }; }; +const addColumnToTimeline = ( + timeline: TimelineModel, + column: ColumnHeaderOptions +): TimelineModel => { + return { ...timeline, columns: [...timeline.columns, column] }; +}; + export const createTimelineLocalStorageEpic = (): Epic< Action, Action, @@ -90,6 +97,21 @@ export const createTimelineLocalStorageEpic = (): Epic< ); }), ignoreElements() + ), + action$.pipe( + ofType(upsertColumn.type), + tap(action => { + const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); + const timelineId: string = get('payload.id', action); + const column: ColumnHeaderOptions = get('payload.column', action); + const modifiedTimeline = addColumnToTimeline(storageTimelines[timelineId], column); + + localStorage.setItem( + 'timelines', + JSON.stringify({ ...storageTimelines, [timelineId]: modifiedTimeline }) + ); + }), + ignoreElements() ) ); }; From a2735b9e9a2161b94e44554e1ef89808f5709357 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 May 2020 13:39:31 +0300 Subject: [PATCH 04/42] Create helpers --- .../store/timeline/epic_local_storage.ts | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 4271273622cd4b..5a1f7765f8e60d 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -8,7 +8,7 @@ import { Action } from 'redux'; import { merge } from 'rxjs'; import { map, filter, ignoreElements, tap, withLatestFrom } from 'rxjs/operators'; import { Epic, ofType } from 'redux-observable'; -import { get } from 'lodash/fp'; +import { get, isEmpty } from 'lodash/fp'; import { createTimeline, updateTimeline, removeColumn, upsertColumn } from './actions'; import { TimelineEpicDependencies } from './epic'; @@ -29,6 +29,21 @@ const addColumnToTimeline = ( return { ...timeline, columns: [...timeline.columns, column] }; }; +const getAllTimelinesFromLocalStorage = () => { + return JSON.parse(localStorage.getItem('timelines') ?? `{}`); +}; + +const addTimelineToLocalStorage = (id: string, timeline: TimelineModel) => { + const timelines = getAllTimelinesFromLocalStorage(); + localStorage.setItem( + 'timelines', + JSON.stringify({ + ...timelines, + [id]: timeline, + }) + ); +}; + export const createTimelineLocalStorageEpic = (): Epic< Action, Action, @@ -51,7 +66,7 @@ export const createTimelineLocalStorageEpic = (): Epic< action$.pipe( ofType(createTimeline.type), withLatestFrom(timeline$), - filter(([action, timeline]) => { + filter(([action]) => { const timelineId: string = get('payload.id', action); if (isPageTimeline(timelineId)) { @@ -60,21 +75,17 @@ export const createTimelineLocalStorageEpic = (): Epic< return false; }), - tap(([action, timeline]) => { - const storageTimelines = localStorage.getItem('timelines'); + tap(([action]) => { + const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); + const timeline: TimelineModel = get('payload', action); - if (!storageTimelines) { - localStorage.setItem( - 'timelines', - JSON.stringify({ - [timelineId]: action.payload, - }) - ); + if (isEmpty(storageTimelines)) { + addTimelineToLocalStorage(timelineId, timeline); } }), map(([action, timeline]) => { - const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); + const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); return updateTimeline({ @@ -86,30 +97,24 @@ export const createTimelineLocalStorageEpic = (): Epic< action$.pipe( ofType(removeColumn.type), tap(action => { - const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); + const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const columnId: string = get('payload.columnId', action); const modifiedTimeline = removeColumnFromTimeline(storageTimelines[timelineId], columnId); - localStorage.setItem( - 'timelines', - JSON.stringify({ ...storageTimelines, [timelineId]: modifiedTimeline }) - ); + addTimelineToLocalStorage(timelineId, modifiedTimeline); }), ignoreElements() ), action$.pipe( ofType(upsertColumn.type), tap(action => { - const storageTimelines = JSON.parse(localStorage.getItem('timelines') ?? ''); + const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const column: ColumnHeaderOptions = get('payload.column', action); const modifiedTimeline = addColumnToTimeline(storageTimelines[timelineId], column); - localStorage.setItem( - 'timelines', - JSON.stringify({ ...storageTimelines, [timelineId]: modifiedTimeline }) - ); + addTimelineToLocalStorage(timelineId, modifiedTimeline); }), ignoreElements() ) From 50223b7bf6f7181da2bd8c80ee5d0c307a05913a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 May 2020 17:19:06 +0300 Subject: [PATCH 05/42] Support multiple page timelines --- .../public/timelines/store/timeline/epic_local_storage.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 5a1f7765f8e60d..7c738ceeeb8a1a 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -15,8 +15,10 @@ import { TimelineEpicDependencies } from './epic'; import { isNotNull } from './helpers'; import { TimelineModel, ColumnHeaderOptions } from './model'; +const timelinePageIds = ['alerts-table', 'signals-page']; + const isPageTimeline = (timelineId: string | undefined) => - timelineId && timelineId.toLowerCase() === 'alerts-table'; + timelineId && timelinePageIds.includes(timelineId); const removeColumnFromTimeline = (timeline: TimelineModel, columnId: string): TimelineModel => { return { ...timeline, columns: timeline.columns.filter(column => column.id !== columnId) }; @@ -80,7 +82,7 @@ export const createTimelineLocalStorageEpic = (): Epic< const timelineId: string = get('payload.id', action); const timeline: TimelineModel = get('payload', action); - if (isEmpty(storageTimelines)) { + if (isEmpty(storageTimelines) || isEmpty(storageTimelines[timelineId])) { addTimelineToLocalStorage(timelineId, timeline); } }), From ce6005689cac84911b72ab82d49a1b82ab82f6ca Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 May 2020 18:06:01 +0300 Subject: [PATCH 06/42] Persist order of columns --- .../store/timeline/epic_local_storage.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 7c738ceeeb8a1a..7014d4250ef363 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -26,9 +26,18 @@ const removeColumnFromTimeline = (timeline: TimelineModel, columnId: string): Ti const addColumnToTimeline = ( timeline: TimelineModel, - column: ColumnHeaderOptions + column: ColumnHeaderOptions, + index: number = 0 ): TimelineModel => { - return { ...timeline, columns: [...timeline.columns, column] }; + const timelineWithoutColumn = removeColumnFromTimeline(timeline, column.id); + return { + ...timelineWithoutColumn, + columns: [ + ...timelineWithoutColumn.columns.slice(0, index), + column, + ...timelineWithoutColumn.columns.slice(index, timeline.columns.length), + ], + }; }; const getAllTimelinesFromLocalStorage = () => { @@ -51,17 +60,7 @@ export const createTimelineLocalStorageEpic = (): Epic< Action, State, TimelineEpicDependencies -> => ( - action$, - state$, - { - selectAllTimelineQuery, - selectNotesByIdSelector, - timelineByIdSelector, - timelineTimeRangeSelector, - apolloClient$, - } -) => { +> => (action$, state$, { timelineByIdSelector }) => { const timeline$ = state$.pipe(map(timelineByIdSelector), filter(isNotNull)); return merge( @@ -114,7 +113,8 @@ export const createTimelineLocalStorageEpic = (): Epic< const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const column: ColumnHeaderOptions = get('payload.column', action); - const modifiedTimeline = addColumnToTimeline(storageTimelines[timelineId], column); + const index: number = get('payload.index', action); + const modifiedTimeline = addColumnToTimeline(storageTimelines[timelineId], column, index); addTimelineToLocalStorage(timelineId, modifiedTimeline); }), From 1048da72b696009b2926b1901b88cf0727ccce0a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 May 2020 18:36:44 +0300 Subject: [PATCH 07/42] Filter page timelines --- .../store/timeline/epic_local_storage.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 7014d4250ef363..52a4ba0e827c9e 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -55,6 +55,14 @@ const addTimelineToLocalStorage = (id: string, timeline: TimelineModel) => { ); }; +const filterPageTimelines = (id: string) => { + if (isPageTimeline(id)) { + return true; + } + + return false; +}; + export const createTimelineLocalStorageEpic = (): Epic< Action, Action, @@ -67,15 +75,7 @@ export const createTimelineLocalStorageEpic = (): Epic< action$.pipe( ofType(createTimeline.type), withLatestFrom(timeline$), - filter(([action]) => { - const timelineId: string = get('payload.id', action); - - if (isPageTimeline(timelineId)) { - return true; - } - - return false; - }), + filter(([action]) => filterPageTimelines(get('payload.id', action))), tap(([action]) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); @@ -97,6 +97,7 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(removeColumn.type), + filter(action => filterPageTimelines(get('payload.id', action))), tap(action => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); @@ -109,6 +110,7 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(upsertColumn.type), + filter(action => filterPageTimelines(get('payload.id', action))), tap(action => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); From 72f6cfb2f4d5102db1647c388b1499a62dc895ae Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 24 May 2020 18:54:36 +0300 Subject: [PATCH 08/42] Persist deltas --- .../store/timeline/epic_local_storage.ts | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 52a4ba0e827c9e..f5aba9a797abed 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -10,9 +10,15 @@ import { map, filter, ignoreElements, tap, withLatestFrom } from 'rxjs/operators import { Epic, ofType } from 'redux-observable'; import { get, isEmpty } from 'lodash/fp'; -import { createTimeline, updateTimeline, removeColumn, upsertColumn } from './actions'; +import { + createTimeline, + updateTimeline, + removeColumn, + upsertColumn, + applyDeltaToColumnWidth, +} from './actions'; import { TimelineEpicDependencies } from './epic'; -import { isNotNull } from './helpers'; +import { isNotNull, applyDeltaToTimelineColumnWidth } from './helpers'; import { TimelineModel, ColumnHeaderOptions } from './model'; const timelinePageIds = ['alerts-table', 'signals-page']; @@ -121,6 +127,25 @@ export const createTimelineLocalStorageEpic = (): Epic< addTimelineToLocalStorage(timelineId, modifiedTimeline); }), ignoreElements() + ), + action$.pipe( + ofType(applyDeltaToColumnWidth.type), + filter(action => filterPageTimelines(get('payload.id', action))), + tap(action => { + const storageTimelines = getAllTimelinesFromLocalStorage(); + const timelineId: string = get('payload.id', action); + const columnId: string = get('payload.columnId', action); + const delta: number = get('payload.delta', action); + const modifiedTimelines = applyDeltaToTimelineColumnWidth({ + id: timelineId, + columnId, + delta, + timelineById: storageTimelines, + }); + + addTimelineToLocalStorage(timelineId, modifiedTimelines[timelineId]); + }), + ignoreElements() ) ); }; From 6cec55b12b668504ff67e0f7fdb2d9849e2bf4d3 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sun, 24 May 2020 19:03:40 +0300 Subject: [PATCH 09/42] Persist reset fields --- .../store/timeline/epic_local_storage.ts | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index f5aba9a797abed..ea7eaacf580091 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -16,9 +16,10 @@ import { removeColumn, upsertColumn, applyDeltaToColumnWidth, + updateColumns, } from './actions'; import { TimelineEpicDependencies } from './epic'; -import { isNotNull, applyDeltaToTimelineColumnWidth } from './helpers'; +import { isNotNull, applyDeltaToTimelineColumnWidth, updateTimelineColumns } from './helpers'; import { TimelineModel, ColumnHeaderOptions } from './model'; const timelinePageIds = ['alerts-table', 'signals-page']; @@ -136,14 +137,31 @@ export const createTimelineLocalStorageEpic = (): Epic< const timelineId: string = get('payload.id', action); const columnId: string = get('payload.columnId', action); const delta: number = get('payload.delta', action); - const modifiedTimelines = applyDeltaToTimelineColumnWidth({ + const timelines = applyDeltaToTimelineColumnWidth({ id: timelineId, columnId, delta, timelineById: storageTimelines, }); - addTimelineToLocalStorage(timelineId, modifiedTimelines[timelineId]); + addTimelineToLocalStorage(timelineId, timelines[timelineId]); + }), + ignoreElements() + ), + action$.pipe( + ofType(updateColumns.type), + filter(action => filterPageTimelines(get('payload.id', action))), + tap(action => { + const storageTimelines = getAllTimelinesFromLocalStorage(); + const timelineId: string = get('payload.id', action); + const columns: ColumnHeaderOptions[] = get('payload.columns', action); + const timelines = updateTimelineColumns({ + id: timelineId, + columns, + timelineById: storageTimelines, + }); + + addTimelineToLocalStorage(timelineId, timelines[timelineId]); }), ignoreElements() ) From 3af9d9b20e08b4fb3504df4aeeb48bf07be293e6 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 25 May 2020 11:56:58 +0300 Subject: [PATCH 10/42] Improve filtering --- .../store/timeline/epic_local_storage.ts | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index ea7eaacf580091..3db1da3e764bea 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -24,8 +24,11 @@ import { TimelineModel, ColumnHeaderOptions } from './model'; const timelinePageIds = ['alerts-table', 'signals-page']; -const isPageTimeline = (timelineId: string | undefined) => - timelineId && timelinePageIds.includes(timelineId); +const isPageTimeline = (timelineId: string | undefined): boolean => + !!(timelineId && timelinePageIds.includes(timelineId)); + +const isItAtimelineAction = (timelineId: string | undefined): boolean => + !!(timelineId && timelineId.toLowerCase().startsWith('timeline')); const removeColumnFromTimeline = (timeline: TimelineModel, columnId: string): TimelineModel => { return { ...timeline, columns: timeline.columns.filter(column => column.id !== columnId) }; @@ -62,14 +65,6 @@ const addTimelineToLocalStorage = (id: string, timeline: TimelineModel) => { ); }; -const filterPageTimelines = (id: string) => { - if (isPageTimeline(id)) { - return true; - } - - return false; -}; - export const createTimelineLocalStorageEpic = (): Epic< Action, Action, @@ -82,7 +77,7 @@ export const createTimelineLocalStorageEpic = (): Epic< action$.pipe( ofType(createTimeline.type), withLatestFrom(timeline$), - filter(([action]) => filterPageTimelines(get('payload.id', action))), + filter(([action]) => isPageTimeline(get('payload.id', action))), tap(([action]) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); @@ -104,7 +99,7 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(removeColumn.type), - filter(action => filterPageTimelines(get('payload.id', action))), + filter(action => isPageTimeline(get('payload.id', action))), tap(action => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); @@ -117,7 +112,7 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(upsertColumn.type), - filter(action => filterPageTimelines(get('payload.id', action))), + filter(action => isPageTimeline(get('payload.id', action))), tap(action => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); @@ -131,7 +126,7 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(applyDeltaToColumnWidth.type), - filter(action => filterPageTimelines(get('payload.id', action))), + filter(action => isPageTimeline(get('payload.id', action))), tap(action => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); @@ -150,7 +145,7 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(updateColumns.type), - filter(action => filterPageTimelines(get('payload.id', action))), + filter(action => isPageTimeline(get('payload.id', action))), tap(action => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); From b7a94a650e1b3e3344005101cfb0f522fee64c5e Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 25 May 2020 11:57:22 +0300 Subject: [PATCH 11/42] Persist itemsPerPage --- .../store/timeline/epic_local_storage.ts | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 3db1da3e764bea..f3761126046150 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -17,9 +17,15 @@ import { upsertColumn, applyDeltaToColumnWidth, updateColumns, + updateItemsPerPage, } from './actions'; import { TimelineEpicDependencies } from './epic'; -import { isNotNull, applyDeltaToTimelineColumnWidth, updateTimelineColumns } from './helpers'; +import { + isNotNull, + applyDeltaToTimelineColumnWidth, + updateTimelineColumns, + updateTimelineItemsPerPage, +} from './helpers'; import { TimelineModel, ColumnHeaderOptions } from './model'; const timelinePageIds = ['alerts-table', 'signals-page']; @@ -159,6 +165,22 @@ export const createTimelineLocalStorageEpic = (): Epic< addTimelineToLocalStorage(timelineId, timelines[timelineId]); }), ignoreElements() + ), + action$.pipe( + ofType(updateItemsPerPage.type), + filter(action => isPageTimeline(get('payload.id', action))), + tap(action => { + const storageTimelines = getAllTimelinesFromLocalStorage(); + const timelineId: string = get('payload.id', action); + const itemsPerPage: number = get('payload.itemsPerPage', action); + const timelines = updateTimelineItemsPerPage({ + id: timelineId, + itemsPerPage, + timelineById: storageTimelines, + }); + addTimelineToLocalStorage(timelineId, timelines[timelineId]); + }), + ignoreElements() ) ); }; From dccf233e78c2850e0420dd619c0a26a451c18139 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 25 May 2020 12:03:36 +0300 Subject: [PATCH 12/42] Persist sort --- .../store/timeline/epic_local_storage.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index f3761126046150..58dc2f5149a2af 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -18,6 +18,7 @@ import { applyDeltaToColumnWidth, updateColumns, updateItemsPerPage, + updateSort, } from './actions'; import { TimelineEpicDependencies } from './epic'; import { @@ -25,8 +26,10 @@ import { applyDeltaToTimelineColumnWidth, updateTimelineColumns, updateTimelineItemsPerPage, + updateTimelineSort, } from './helpers'; import { TimelineModel, ColumnHeaderOptions } from './model'; +import { Sort } from '../../components/timeline/body/sort'; const timelinePageIds = ['alerts-table', 'signals-page']; @@ -181,6 +184,22 @@ export const createTimelineLocalStorageEpic = (): Epic< addTimelineToLocalStorage(timelineId, timelines[timelineId]); }), ignoreElements() + ), + action$.pipe( + ofType(updateSort.type), + filter(action => isPageTimeline(get('payload.id', action))), + tap(action => { + const storageTimelines = getAllTimelinesFromLocalStorage(); + const timelineId: string = get('payload.id', action); + const sort: Sort = get('payload.sort', action); + const timelines = updateTimelineSort({ + id: timelineId, + sort, + timelineById: storageTimelines, + }); + addTimelineToLocalStorage(timelineId, timelines[timelineId]); + }), + ignoreElements() ) ); }; From 7956547ee9d452b7903e0c4fec61fb85f6408f5d Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 25 May 2020 12:11:48 +0300 Subject: [PATCH 13/42] Use helpers --- .../store/timeline/epic_local_storage.ts | 39 +++++++------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 58dc2f5149a2af..903ee1ac0b8733 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -27,6 +27,8 @@ import { updateTimelineColumns, updateTimelineItemsPerPage, updateTimelineSort, + removeTimelineColumn, + upsertTimelineColumn, } from './helpers'; import { TimelineModel, ColumnHeaderOptions } from './model'; import { Sort } from '../../components/timeline/body/sort'; @@ -39,26 +41,6 @@ const isPageTimeline = (timelineId: string | undefined): boolean => const isItAtimelineAction = (timelineId: string | undefined): boolean => !!(timelineId && timelineId.toLowerCase().startsWith('timeline')); -const removeColumnFromTimeline = (timeline: TimelineModel, columnId: string): TimelineModel => { - return { ...timeline, columns: timeline.columns.filter(column => column.id !== columnId) }; -}; - -const addColumnToTimeline = ( - timeline: TimelineModel, - column: ColumnHeaderOptions, - index: number = 0 -): TimelineModel => { - const timelineWithoutColumn = removeColumnFromTimeline(timeline, column.id); - return { - ...timelineWithoutColumn, - columns: [ - ...timelineWithoutColumn.columns.slice(0, index), - column, - ...timelineWithoutColumn.columns.slice(index, timeline.columns.length), - ], - }; -}; - const getAllTimelinesFromLocalStorage = () => { return JSON.parse(localStorage.getItem('timelines') ?? `{}`); }; @@ -113,9 +95,13 @@ export const createTimelineLocalStorageEpic = (): Epic< const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const columnId: string = get('payload.columnId', action); - const modifiedTimeline = removeColumnFromTimeline(storageTimelines[timelineId], columnId); + const timelines = removeTimelineColumn({ + id: timelineId, + columnId, + timelineById: storageTimelines, + }); - addTimelineToLocalStorage(timelineId, modifiedTimeline); + addTimelineToLocalStorage(timelineId, timelines[timelineId]); }), ignoreElements() ), @@ -127,9 +113,14 @@ export const createTimelineLocalStorageEpic = (): Epic< const timelineId: string = get('payload.id', action); const column: ColumnHeaderOptions = get('payload.column', action); const index: number = get('payload.index', action); - const modifiedTimeline = addColumnToTimeline(storageTimelines[timelineId], column, index); + const timelines = upsertTimelineColumn({ + id: timelineId, + column, + index, + timelineById: storageTimelines, + }); - addTimelineToLocalStorage(timelineId, modifiedTimeline); + addTimelineToLocalStorage(timelineId, timelines[timelineId]); }), ignoreElements() ), From 0ee69274b2b3261bd7020d4ce76fa34a53a7126b Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 25 May 2020 13:26:31 +0300 Subject: [PATCH 14/42] Fix prettier linting warnings --- .../store/timeline/epic_local_storage.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 903ee1ac0b8733..faf3860180d017 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -90,8 +90,8 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(removeColumn.type), - filter(action => isPageTimeline(get('payload.id', action))), - tap(action => { + filter((action) => isPageTimeline(get('payload.id', action))), + tap((action) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const columnId: string = get('payload.columnId', action); @@ -107,8 +107,8 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(upsertColumn.type), - filter(action => isPageTimeline(get('payload.id', action))), - tap(action => { + filter((action) => isPageTimeline(get('payload.id', action))), + tap((action) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const column: ColumnHeaderOptions = get('payload.column', action); @@ -126,8 +126,8 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(applyDeltaToColumnWidth.type), - filter(action => isPageTimeline(get('payload.id', action))), - tap(action => { + filter((action) => isPageTimeline(get('payload.id', action))), + tap((action) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const columnId: string = get('payload.columnId', action); @@ -145,8 +145,8 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(updateColumns.type), - filter(action => isPageTimeline(get('payload.id', action))), - tap(action => { + filter((action) => isPageTimeline(get('payload.id', action))), + tap((action) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const columns: ColumnHeaderOptions[] = get('payload.columns', action); @@ -162,8 +162,8 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(updateItemsPerPage.type), - filter(action => isPageTimeline(get('payload.id', action))), - tap(action => { + filter((action) => isPageTimeline(get('payload.id', action))), + tap((action) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const itemsPerPage: number = get('payload.itemsPerPage', action); @@ -178,8 +178,8 @@ export const createTimelineLocalStorageEpic = (): Epic< ), action$.pipe( ofType(updateSort.type), - filter(action => isPageTimeline(get('payload.id', action))), - tap(action => { + filter((action) => isPageTimeline(get('payload.id', action))), + tap((action) => { const storageTimelines = getAllTimelinesFromLocalStorage(); const timelineId: string = get('payload.id', action); const sort: Sort = get('payload.sort', action); From 96fdc65568e5eadafc61389259f9c72945de12d2 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 26 May 2020 17:11:57 +0300 Subject: [PATCH 15/42] Refactor code --- .../timelines/store/timeline/reducer.ts | 3 +- .../public/common/lib/local_storage/index.tsx | 25 +++ .../store/timeline/epic_local_storage.ts | 172 ++---------------- 3 files changed, 45 insertions(+), 155 deletions(-) create mode 100644 x-pack/plugins/siem/public/common/lib/local_storage/index.tsx diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index a8ae39527cdbfc..4f7b9078aaff7c 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -98,9 +98,10 @@ import { } from './helpers'; import { TimelineState, EMPTY_TIMELINE_BY_ID } from './types'; +import { getAllTimelines } from '../../../common/lib/local_storage'; export const initialTimelineState: TimelineState = { - timelineById: EMPTY_TIMELINE_BY_ID, + timelineById: { ...EMPTY_TIMELINE_BY_ID, ...getAllTimelines() }, autoSavedWarningMsg: { timelineId: null, newTimelineModel: null, diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx b/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx new file mode 100644 index 00000000000000..d7bc2f94e9f61e --- /dev/null +++ b/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx @@ -0,0 +1,25 @@ +/* + * 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. + */ + +const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; + +const getItem = (id: string) => JSON.parse(localStorage.getItem(id) ?? `{}`); + +const setItem = (id: string, item: unknown) => localStorage.setItem(id, JSON.stringify(item)); + +const getAllTimelines = () => { + return getItem(LOCAL_STORAGE_TIMELINE_KEY); +}; + +const addTimeline = (id: string, timeline: unknown) => { + const timelines = getAllTimelines(); + setItem(LOCAL_STORAGE_TIMELINE_KEY, { + ...timelines, + [id]: timeline, + }); +}; + +export { getItem, setItem, getAllTimelines, addTimeline }; diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index faf3860180d017..1557ae1442f601 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -6,13 +6,11 @@ import { Action } from 'redux'; import { merge } from 'rxjs'; -import { map, filter, ignoreElements, tap, withLatestFrom } from 'rxjs/operators'; -import { Epic, ofType } from 'redux-observable'; -import { get, isEmpty } from 'lodash/fp'; +import { map, filter, ignoreElements, tap, withLatestFrom, delay } from 'rxjs/operators'; +import { Epic } from 'redux-observable'; +import { get } from 'lodash/fp'; import { - createTimeline, - updateTimeline, removeColumn, upsertColumn, applyDeltaToColumnWidth, @@ -21,41 +19,22 @@ import { updateSort, } from './actions'; import { TimelineEpicDependencies } from './epic'; -import { - isNotNull, - applyDeltaToTimelineColumnWidth, - updateTimelineColumns, - updateTimelineItemsPerPage, - updateTimelineSort, - removeTimelineColumn, - upsertTimelineColumn, -} from './helpers'; -import { TimelineModel, ColumnHeaderOptions } from './model'; -import { Sort } from '../../components/timeline/body/sort'; +import { isNotNull } from './helpers'; +import { addTimeline } from '../../../common/lib/local_storage'; const timelinePageIds = ['alerts-table', 'signals-page']; +const timelineActionTypes = [ + removeColumn.type, + upsertColumn.type, + applyDeltaToColumnWidth.type, + updateColumns.type, + updateItemsPerPage.type, + updateSort.type, +]; const isPageTimeline = (timelineId: string | undefined): boolean => !!(timelineId && timelinePageIds.includes(timelineId)); -const isItAtimelineAction = (timelineId: string | undefined): boolean => - !!(timelineId && timelineId.toLowerCase().startsWith('timeline')); - -const getAllTimelinesFromLocalStorage = () => { - return JSON.parse(localStorage.getItem('timelines') ?? `{}`); -}; - -const addTimelineToLocalStorage = (id: string, timeline: TimelineModel) => { - const timelines = getAllTimelinesFromLocalStorage(); - localStorage.setItem( - 'timelines', - JSON.stringify({ - ...timelines, - [id]: timeline, - }) - ); -}; - export const createTimelineLocalStorageEpic = (): Epic< Action, Action, @@ -66,130 +45,15 @@ export const createTimelineLocalStorageEpic = (): Epic< return merge( action$.pipe( - ofType(createTimeline.type), + delay(500), withLatestFrom(timeline$), filter(([action]) => isPageTimeline(get('payload.id', action))), - tap(([action]) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - const timeline: TimelineModel = get('payload', action); - - if (isEmpty(storageTimelines) || isEmpty(storageTimelines[timelineId])) { - addTimelineToLocalStorage(timelineId, timeline); + tap(([action, timelineById]) => { + if (timelineActionTypes.includes(action.type)) { + const timelineId: string = get('payload.id', action); + addTimeline(timelineId, timelineById[timelineId]); } }), - map(([action, timeline]) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - - return updateTimeline({ - id: timelineId, - timeline: { ...timeline[timelineId], ...storageTimelines[timelineId] }, - }); - }) - ), - action$.pipe( - ofType(removeColumn.type), - filter((action) => isPageTimeline(get('payload.id', action))), - tap((action) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - const columnId: string = get('payload.columnId', action); - const timelines = removeTimelineColumn({ - id: timelineId, - columnId, - timelineById: storageTimelines, - }); - - addTimelineToLocalStorage(timelineId, timelines[timelineId]); - }), - ignoreElements() - ), - action$.pipe( - ofType(upsertColumn.type), - filter((action) => isPageTimeline(get('payload.id', action))), - tap((action) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - const column: ColumnHeaderOptions = get('payload.column', action); - const index: number = get('payload.index', action); - const timelines = upsertTimelineColumn({ - id: timelineId, - column, - index, - timelineById: storageTimelines, - }); - - addTimelineToLocalStorage(timelineId, timelines[timelineId]); - }), - ignoreElements() - ), - action$.pipe( - ofType(applyDeltaToColumnWidth.type), - filter((action) => isPageTimeline(get('payload.id', action))), - tap((action) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - const columnId: string = get('payload.columnId', action); - const delta: number = get('payload.delta', action); - const timelines = applyDeltaToTimelineColumnWidth({ - id: timelineId, - columnId, - delta, - timelineById: storageTimelines, - }); - - addTimelineToLocalStorage(timelineId, timelines[timelineId]); - }), - ignoreElements() - ), - action$.pipe( - ofType(updateColumns.type), - filter((action) => isPageTimeline(get('payload.id', action))), - tap((action) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - const columns: ColumnHeaderOptions[] = get('payload.columns', action); - const timelines = updateTimelineColumns({ - id: timelineId, - columns, - timelineById: storageTimelines, - }); - - addTimelineToLocalStorage(timelineId, timelines[timelineId]); - }), - ignoreElements() - ), - action$.pipe( - ofType(updateItemsPerPage.type), - filter((action) => isPageTimeline(get('payload.id', action))), - tap((action) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - const itemsPerPage: number = get('payload.itemsPerPage', action); - const timelines = updateTimelineItemsPerPage({ - id: timelineId, - itemsPerPage, - timelineById: storageTimelines, - }); - addTimelineToLocalStorage(timelineId, timelines[timelineId]); - }), - ignoreElements() - ), - action$.pipe( - ofType(updateSort.type), - filter((action) => isPageTimeline(get('payload.id', action))), - tap((action) => { - const storageTimelines = getAllTimelinesFromLocalStorage(); - const timelineId: string = get('payload.id', action); - const sort: Sort = get('payload.sort', action); - const timelines = updateTimelineSort({ - id: timelineId, - sort, - timelineById: storageTimelines, - }); - addTimelineToLocalStorage(timelineId, timelines[timelineId]); - }), ignoreElements() ) ); From 54e092621ff85b9c45fe677cc225769f4bcd01ef Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 28 May 2020 11:04:20 +0300 Subject: [PATCH 16/42] Improve filtering to include any timeline expect the flyout --- .../public/timelines/store/timeline/epic_local_storage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 1557ae1442f601..447e36e21ffefc 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -22,7 +22,6 @@ import { TimelineEpicDependencies } from './epic'; import { isNotNull } from './helpers'; import { addTimeline } from '../../../common/lib/local_storage'; -const timelinePageIds = ['alerts-table', 'signals-page']; const timelineActionTypes = [ removeColumn.type, upsertColumn.type, @@ -33,7 +32,8 @@ const timelineActionTypes = [ ]; const isPageTimeline = (timelineId: string | undefined): boolean => - !!(timelineId && timelinePageIds.includes(timelineId)); + // Is not a flyout timeline + !(timelineId && timelineId.toLowerCase().startsWith('timeline')); export const createTimelineLocalStorageEpic = (): Epic< Action, From cec55f08a9c1a8cb09b15169eb38947189cd78d4 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 28 May 2020 11:35:04 +0300 Subject: [PATCH 17/42] Dynamic ids to alert tables --- .../alerts/pages/detection_engine/detection_engine.tsx | 3 +++ .../common/components/alerts_viewer/alerts_table.tsx | 6 +++--- .../public/common/components/alerts_viewer/index.tsx | 7 ++++--- .../public/common/components/alerts_viewer/types.ts | 4 +++- .../hosts/pages/navigation/alerts_query_tab_body.tsx | 4 +++- .../hosts/pages/navigation/events_query_tab_body.tsx | 2 +- .../network/pages/navigation/alerts_query_tab_body.tsx | 4 +++- 7 files changed, 20 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx index e3eb4666522ad6..7517f6cebc6b4c 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx @@ -37,6 +37,8 @@ import { DetectionEngineHeaderPage } from '../../components/detection_engine_hea import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; +const ALERTS_TABLE_ID = 'alerts-page-alerts'; + export const DetectionEnginePageComponent: React.FC = ({ filters, query, @@ -138,6 +140,7 @@ export const DetectionEnginePageComponent: React.FC = ({ /> = ({ endDate, startDate, pageFilters = [] }) => { +const AlertsTableComponent: React.FC = ({ id, endDate, startDate, pageFilters = [] }) => { const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]); const { initializeTimeline } = useManageTimeline(); @@ -75,7 +75,7 @@ const AlertsTableComponent: React.FC = ({ endDate, startDate, pageFilters pageFilters={alertsFilter} defaultModel={alertsDefaultModel} end={endDate} - id={ALERTS_TABLE_ID} + id={id} start={startDate} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx index 29f4bdff92ad60..adb613e471222b 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx @@ -7,7 +7,7 @@ import React, { useEffect, useCallback, useMemo } from 'react'; import numeral from '@elastic/numeral'; import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; -import { AlertsComponentsQueryProps } from './types'; +import { AlertsComponentsProps } from './types'; import { AlertsTable } from './alerts_table'; import * as i18n from './translations'; import { useUiSetting$ } from '../../lib/kibana'; @@ -17,6 +17,7 @@ import { MatrixHisrogramConfigs } from '../matrix_histogram/types'; const ID = 'alertsOverTimeQuery'; export const AlertsView = ({ + tableId, deleteQuery, endDate, filterQuery, @@ -24,7 +25,7 @@ export const AlertsView = ({ setQuery, startDate, type, -}: AlertsComponentsQueryProps) => { +}: AlertsComponentsProps) => { const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); const getSubtitle = useCallback( (totalCount: number) => @@ -60,7 +61,7 @@ export const AlertsView = ({ type={type} {...alertsHistogramConfigs} /> - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts index 2bc33aaf1bae77..6a89b28e4a6c5a 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts @@ -10,11 +10,13 @@ import { NetworkComponentQueryProps } from '../../../network/pages/navigation/ty import { MatrixHistogramOption } from '../matrix_histogram/types'; type CommonQueryProps = HostsComponentsQueryProps | NetworkComponentQueryProps; -export interface AlertsComponentsQueryProps + +export interface AlertsComponentsProps extends Pick< CommonQueryProps, 'deleteQuery' | 'endDate' | 'filterQuery' | 'skip' | 'setQuery' | 'startDate' | 'type' > { + tableId: string; pageFilters: Filter[]; stackByOptions?: MatrixHistogramOption[]; defaultFilters?: Filter[]; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx index a0d8df6b87514b..d779bb7512a049 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx @@ -10,6 +10,8 @@ import { Filter } from '../../../../../../../src/plugins/data/public'; import { AlertsView } from '../../../common/components/alerts_viewer'; import { AlertsComponentQueryProps } from './types'; +const ALERTS_TABLE_ID = 'hosts-page-external-alerts'; + export const filterHostData: Filter[] = [ { query: { @@ -48,7 +50,7 @@ export const HostAlertsQueryTabBody = React.memo((alertsProps: AlertsComponentQu [pageFilters] ); - return ; + return ; }); HostAlertsQueryTabBody.displayName = 'HostAlertsQueryTabBody'; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx index b8ec269508442b..25387386171181 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx @@ -17,7 +17,7 @@ import { MatrixHistogramContainer } from '../../../common/components/matrix_hist import * as i18n from '../translations'; import { HistogramType } from '../../../graphql/types'; -const HOSTS_PAGE_TIMELINE_ID = 'hosts-page'; +const HOSTS_PAGE_TIMELINE_ID = 'hosts-page-events'; const EVENTS_HISTOGRAM_ID = 'eventsOverTimeQuery'; export const eventsStackByOptions: MatrixHistogramOption[] = [ diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx index c5f59e751ca9ae..1c331d12ac84ea 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx @@ -10,6 +10,8 @@ import { Filter } from '../../../../../../../src/plugins/data/common/es_query'; import { AlertsView } from '../../../common/components/alerts_viewer'; import { NetworkComponentQueryProps } from './types'; +const ALERTS_TABLE_ID = 'network-page-external-alerts'; + export const filterNetworkData: Filter[] = [ { query: { @@ -62,7 +64,7 @@ export const filterNetworkData: Filter[] = [ ]; export const NetworkAlertsQueryTabBody = React.memo((alertsProps: NetworkComponentQueryProps) => ( - + )); NetworkAlertsQueryTabBody.displayName = 'NetworkAlertsQueryTabBody'; From 637189a64793f00c7ccfd0b1ef014b23c800c466 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 28 May 2020 11:41:13 +0300 Subject: [PATCH 18/42] Move type to types --- .../public/timelines/store/timeline/epic.ts | 16 +++------------- .../public/timelines/store/timeline/types.ts | 13 +++++++++++++ .../store/timeline/epic_local_storage.ts | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts index 986c65d7a93a31..2155dc804aa7ed 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts @@ -15,7 +15,7 @@ import { } from 'lodash/fp'; import { Action } from 'redux'; import { Epic } from 'redux-observable'; -import { from, Observable, empty, merge } from 'rxjs'; +import { from, empty, merge } from 'rxjs'; import { filter, map, @@ -34,16 +34,14 @@ import { MatchAllFilter, } from '../../../../../../.../../../src/plugins/data/public'; import { TimelineStatus } from '../../../../common/types/timeline'; +import { inputsModel } from '../../../common/store/inputs'; import { TimelineType, TimelineInput, ResponseTimeline, TimelineResult, } from '../../../graphql/types'; -import { AppApolloClient } from '../../../common/lib/lib'; import { addError } from '../../../common/store/app/actions'; -import { NotesById } from '../../../common/store/app/model'; -import { inputsModel } from '../../../common/store/inputs'; import { applyKqlFilterQuery, @@ -80,18 +78,10 @@ import { epicPersistTimelineFavorite, timelineFavoriteActionsType } from './epic import { isNotNull } from './helpers'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import { myEpicTimelineId } from './my_epic_timeline_id'; -import { ActionTimeline, TimelineById } from './types'; +import { ActionTimeline, TimelineEpicDependencies } from './types'; import { persistTimeline } from '../../containers/api'; import { ALL_TIMELINE_QUERY_ID } from '../../containers/all'; -export interface TimelineEpicDependencies { - timelineByIdSelector: (state: State) => TimelineById; - timelineTimeRangeSelector: (state: State) => inputsModel.TimeRange; - selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery; - selectNotesByIdSelector: (state: State) => NotesById; - apolloClient$: Observable; -} - const timelineActionsType = [ applyKqlFilterQuery.type, addProvider.type, diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts index 1cc4517d2c9642..ed4572b3be1897 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts @@ -4,6 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import { Action } from 'redux'; +import { Observable } from 'rxjs'; + +import { AppApolloClient } from '../../../common/lib/lib'; +import { inputsModel } from '../../../common/store/inputs'; +import { NotesById } from '../../../common/store/app/model'; import { TimelineModel } from './model'; export interface AutoSavedWarningMsg { @@ -32,3 +37,11 @@ export interface ActionTimeline extends Action { noteId: string; }; } + +export interface TimelineEpicDependencies { + timelineByIdSelector: (state: State) => TimelineById; + timelineTimeRangeSelector: (state: State) => inputsModel.TimeRange; + selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery; + selectNotesByIdSelector: (state: State) => NotesById; + apolloClient$: Observable; +} diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 447e36e21ffefc..46f596d508cfca 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -18,7 +18,7 @@ import { updateItemsPerPage, updateSort, } from './actions'; -import { TimelineEpicDependencies } from './epic'; +import { TimelineEpicDependencies } from './types'; import { isNotNull } from './helpers'; import { addTimeline } from '../../../common/lib/local_storage'; From 0bbd5ab43ffd98fd4b4b621571857a9d4d682c65 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 28 May 2020 12:46:40 +0300 Subject: [PATCH 19/42] Improve localStorage --- .../security_solution/public/app/app.tsx | 14 ++++++++- .../public/common/store/store.ts | 3 ++ .../timelines/store/timeline/reducer.ts | 3 +- .../public/timelines/store/timeline/types.ts | 2 ++ .../public/common/lib/local_storage/index.tsx | 29 +++++++++++-------- .../public/common/lib/local_storage/types.ts | 10 +++++++ .../store/timeline/epic_local_storage.ts | 5 ++-- 7 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 x-pack/plugins/siem/public/common/lib/local_storage/types.ts diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 50a24ef012b8b9..771ebb2ab321a9 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -33,6 +33,7 @@ import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; import { ApolloClientContext } from '../common/utils/apollo_context'; import { SecuritySubPlugins } from './types'; +import { createSiemLocalStorage } from '../common/lib/local_storage'; interface AppPluginRootComponentProps { apolloClient: AppApolloClient; @@ -79,11 +80,22 @@ const StartAppComponent: FC = ({ subPlugins, ...libs }) => { const { i18n } = useKibana().services; const history = createHashHistory(); const libs$ = new BehaviorSubject(libs); + const storage = createSiemLocalStorage(); const store = createStore( - createInitialState(subPluginsStore.initialState), + createInitialState({ + ...subPluginsStore.initialState, + timeline: { + ...subPluginsStore.initialState.timeline, + timelineById: { + ...subPluginsStore.initialState.timeline.timelineById, + ...(storage.getAllTimelines() ?? {}), + }, + }, + }), subPluginsStore.reducer, libs$.pipe(pluck('apolloClient')), + storage, subPluginsStore.middlewares ); diff --git a/x-pack/plugins/security_solution/public/common/store/store.ts b/x-pack/plugins/security_solution/public/common/store/store.ts index 276dcdcaedb854..a7f5caf2c77d0b 100644 --- a/x-pack/plugins/security_solution/public/common/store/store.ts +++ b/x-pack/plugins/security_solution/public/common/store/store.ts @@ -28,6 +28,7 @@ import { AppApolloClient } from '../lib/lib'; import { AppAction } from './actions'; import { Immutable } from '../../../common/endpoint/types'; import { State } from './types'; +import { SiemStorage } from '../lib/local_storage'; type ComposeType = typeof compose; declare global { @@ -48,6 +49,7 @@ export const createStore = ( state: PreloadedState, pluginsReducer: SubPluginsInitReducer, apolloClient: Observable, + storage: SiemStorage, additionalMiddleware?: Array>>> ): Store => { const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; @@ -58,6 +60,7 @@ export const createStore = ( selectNotesByIdSelector: appSelectors.selectNotesByIdSelector, timelineByIdSelector: timelineSelectors.timelineByIdSelector, timelineTimeRangeSelector: inputsSelectors.timelineTimeRangeSelector, + storage, }; const epicMiddleware = createEpicMiddleware( diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index 4f7b9078aaff7c..e9c203d7ac4381 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -98,10 +98,9 @@ import { } from './helpers'; import { TimelineState, EMPTY_TIMELINE_BY_ID } from './types'; -import { getAllTimelines } from '../../../common/lib/local_storage'; export const initialTimelineState: TimelineState = { - timelineById: { ...EMPTY_TIMELINE_BY_ID, ...getAllTimelines() }, + timelineById: { ...EMPTY_TIMELINE_BY_ID }, autoSavedWarningMsg: { timelineId: null, newTimelineModel: null, diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts index ed4572b3be1897..50057f3be08c92 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts @@ -6,6 +6,7 @@ import { Action } from 'redux'; import { Observable } from 'rxjs'; +import { SiemStorage } from '../../../common/lib/local_storage'; import { AppApolloClient } from '../../../common/lib/lib'; import { inputsModel } from '../../../common/store/inputs'; import { NotesById } from '../../../common/store/app/model'; @@ -44,4 +45,5 @@ export interface TimelineEpicDependencies { selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery; selectNotesByIdSelector: (state: State) => NotesById; apolloClient$: Observable; + storage: SiemStorage; } diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx b/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx index d7bc2f94e9f61e..41349130857601 100644 --- a/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx +++ b/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx @@ -4,22 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; +import { SiemStorage } from './types'; + const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; -const getItem = (id: string) => JSON.parse(localStorage.getItem(id) ?? `{}`); +export const createSiemLocalStorage = (): SiemStorage => { + const storage = new Storage(localStorage); -const setItem = (id: string, item: unknown) => localStorage.setItem(id, JSON.stringify(item)); + const getAllTimelines: SiemStorage['getAllTimelines'] = () => { + return storage.get(LOCAL_STORAGE_TIMELINE_KEY); + }; -const getAllTimelines = () => { - return getItem(LOCAL_STORAGE_TIMELINE_KEY); -}; + const addTimeline: SiemStorage['addTimeline'] = (id, timeline) => { + const timelines = getAllTimelines() ?? {}; + storage.set(LOCAL_STORAGE_TIMELINE_KEY, { + ...timelines, + [id]: timeline, + }); + }; -const addTimeline = (id: string, timeline: unknown) => { - const timelines = getAllTimelines(); - setItem(LOCAL_STORAGE_TIMELINE_KEY, { - ...timelines, - [id]: timeline, - }); + return { getAllTimelines, addTimeline }; }; -export { getItem, setItem, getAllTimelines, addTimeline }; +export { SiemStorage }; diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/types.ts b/x-pack/plugins/siem/public/common/lib/local_storage/types.ts new file mode 100644 index 00000000000000..72a79b776acbdb --- /dev/null +++ b/x-pack/plugins/siem/public/common/lib/local_storage/types.ts @@ -0,0 +1,10 @@ +/* + * 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. + */ + +export interface SiemStorage { + getAllTimelines: () => object | null; + addTimeline: (id: string, timeline: unknown) => void; +} diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index 46f596d508cfca..fd0a78db8c1efa 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -20,7 +20,6 @@ import { } from './actions'; import { TimelineEpicDependencies } from './types'; import { isNotNull } from './helpers'; -import { addTimeline } from '../../../common/lib/local_storage'; const timelineActionTypes = [ removeColumn.type, @@ -40,7 +39,7 @@ export const createTimelineLocalStorageEpic = (): Epic< Action, State, TimelineEpicDependencies -> => (action$, state$, { timelineByIdSelector }) => { +> => (action$, state$, { timelineByIdSelector, storage }) => { const timeline$ = state$.pipe(map(timelineByIdSelector), filter(isNotNull)); return merge( @@ -51,7 +50,7 @@ export const createTimelineLocalStorageEpic = (): Epic< tap(([action, timelineById]) => { if (timelineActionTypes.includes(action.type)) { const timelineId: string = get('payload.id', action); - addTimeline(timelineId, timelineById[timelineId]); + storage.addTimeline(timelineId, timelineById[timelineId]); } }), ignoreElements() From 660344f58e2aad0d20add14f70b46e2220182329 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 28 May 2020 13:34:22 +0300 Subject: [PATCH 20/42] Improve tests --- .../index.test.tsx | 6 +++-- .../error_toast_dispatcher/index.test.tsx | 12 ++++++--- .../common/components/inspect/index.test.tsx | 18 ++++++++++--- .../components/stat_items/index.test.tsx | 15 +++++++++-- .../super_date_picker/index.test.tsx | 17 +++++++++--- .../common/components/top_n/index.test.tsx | 5 +++- .../mock/endpoint/app_context_render.tsx | 27 ++++++++++++------- .../public/common/mock/index.ts | 1 + .../public/common/mock/test_providers.tsx | 6 +++-- .../view/test_helpers/render_alert_page.tsx | 15 +++++++++-- .../authentications_table/index.test.tsx | 12 ++++++--- .../components/hosts_table/index.test.tsx | 6 +++-- .../public/hosts/pages/hosts.test.tsx | 9 ++++++- .../components/ip_overview/index.test.tsx | 6 +++-- .../components/kpi_network/index.test.tsx | 12 ++++++--- .../network_dns_table/index.test.tsx | 6 +++-- .../network_http_table/index.test.tsx | 6 +++-- .../index.test.tsx | 6 +++-- .../network_top_n_flow_table/index.test.tsx | 6 +++-- .../components/tls_table/index.test.tsx | 6 +++-- .../components/users_table/index.test.tsx | 6 +++-- .../network/pages/ip_details/index.test.tsx | 6 +++-- .../public/network/pages/network.test.tsx | 9 ++++++- .../components/overview_host/index.test.tsx | 6 +++-- .../overview_network/index.test.tsx | 6 +++-- .../components/flyout/index.test.tsx | 14 +++++++--- .../timeline/properties/index.test.tsx | 6 +++-- .../public/common/mock/mock_local_storage.ts | 10 +++++++ 28 files changed, 197 insertions(+), 63 deletions(-) create mode 100644 x-pack/plugins/siem/public/common/mock/mock_local_storage.ts diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx index 18c0032f58c3c3..80c6fc243433b4 100644 --- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx @@ -12,6 +12,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../mock'; import { createStore, State } from '../../store'; import { AddFilterToGlobalSearchBar } from '.'; @@ -33,10 +34,11 @@ jest.mock('../../lib/kibana', () => ({ describe('AddFilterToGlobalSearchBar Component', () => { const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); mockAddFilters.mockClear(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx index 39b17f7008e64b..cca68d26287bb1 100644 --- a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx @@ -8,7 +8,12 @@ import { shallow } from 'enzyme'; import React from 'react'; import { Provider } from 'react-redux'; -import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER } from '../../mock'; +import { + apolloClientObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, +} from '../../mock'; import { createStore } from '../../store/store'; import { ErrorToastDispatcher } from '.'; @@ -16,10 +21,11 @@ import { State } from '../../store/types'; describe('Error Toast Dispatcher', () => { const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx index d147f0224fdb65..659c58f76d0d1c 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx @@ -14,6 +14,7 @@ import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../mock'; import { createStore, State } from '../../store'; import { UpdateQueryParams, upsertQuery } from '../../store/inputs/helpers'; @@ -25,6 +26,7 @@ describe('Inspect Button', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); const refetch = jest.fn(); const state: State = mockGlobalState; + const siemLocalStorageMock = createSiemLocalStorageMock(); const newQuery: UpdateQueryParams = { inputId: 'global', id: 'myQuery', @@ -34,13 +36,18 @@ describe('Inspect Button', () => { state: state.inputs, }; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); describe('Render', () => { beforeEach(() => { const myState = cloneDeep(state); myState.inputs = upsertQuery(newQuery); - store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore( + myState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock + ); }); test('Eui Empty Button', () => { const wrapper = mount( @@ -144,7 +151,12 @@ describe('Inspect Button', () => { response: ['my response'], }; myState.inputs = upsertQuery(myQuery); - store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore( + myState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock + ); }); test('Open Inspect Modal', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx index f46697834d0e32..9e7ff2cbb72bdb 100644 --- a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx @@ -30,7 +30,12 @@ import { mockNoChartMappings, mockNarrowDateRange, } from '../../../network/components/kpi_network/mock'; -import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER } from '../../mock'; +import { + mockGlobalState, + apolloClientObservable, + SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, +} from '../../mock'; import { State, createStore } from '../../store'; import { Provider as ReduxStoreProvider } from 'react-redux'; import { KpiNetworkData, KpiHostsData } from '../../../graphql/types'; @@ -49,7 +54,13 @@ jest.mock('../charts/barchart', () => { describe('Stat Items Component', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); const state: State = mockGlobalState; - const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + const store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock + ); describe.each([ [ diff --git a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx index 04cb348c3f9cd7..703683e7e93fd9 100644 --- a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx @@ -10,7 +10,12 @@ import { Provider as ReduxStoreProvider } from 'react-redux'; import { DEFAULT_TIMEPICKER_QUICK_RANGES } from '../../../../common/constants'; import { useUiSetting$ } from '../../lib/kibana'; -import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER } from '../../mock'; +import { + apolloClientObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, +} from '../../mock'; import { createUseUiSetting$Mock } from '../../mock/kibana_react'; import { createStore, State } from '../../store'; @@ -75,11 +80,17 @@ const timepickerRanges = [ describe('SIEM Super Date Picker', () => { describe('#SuperDatePicker', () => { const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock + ); beforeEach(() => { jest.clearAllMocks(); - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); mockUseUiSetting$.mockImplementation((key, defaultValue) => { const useUiSetting$Mock = createUseUiSetting$Mock(); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index aa310d63ab2836..08d48e10e6d80d 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -13,6 +13,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../mock'; import { createKibanaCoreStartMock } from '../../mock/kibana_core'; import { FilterManager } from '../../../../../../../src/plugins/data/public'; @@ -141,7 +142,9 @@ const state: State = { }, }, }; -const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + +const siemLocalStorageMock = createSiemLocalStorageMock(); +const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); describe('StatefulTopN', () => { // Suppress warnings about "react-beautiful-dnd" diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index d930136b3c0c46..2518a34a815dee 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -19,7 +19,7 @@ import { alertMiddlewareFactory } from '../../../endpoint_alerts/store/middlewar import { AppRootProvider } from './app_root_provider'; import { managementMiddlewareFactory } from '../../../management/store/middleware'; import { createKibanaContextProviderMock } from '../kibana_react'; -import { SUB_PLUGINS_REDUCER, mockGlobalState } from '..'; +import { SUB_PLUGINS_REDUCER, mockGlobalState, createSiemLocalStorageMock } from '..'; type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; @@ -56,14 +56,23 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { const coreStart = coreMock.createStart({ basePath: '/mock' }); const depsStart = depsStartMock(); const middlewareSpy = createSpyMiddleware(); - const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable, [ - substateMiddlewareFactory( - (globalState) => globalState.alertList, - alertMiddlewareFactory(coreStart, depsStart) - ), - ...managementMiddlewareFactory(coreStart, depsStart), - middlewareSpy.actionSpyMiddleware, - ]); + const siemLocalStorageMock = createSiemLocalStorageMock(); + + const store = createStore( + mockGlobalState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock, + [ + substateMiddlewareFactory( + (globalState) => globalState.alertList, + alertMiddlewareFactory(coreStart, depsStart) + ), + ...managementMiddlewareFactory(coreStart, depsStart), + middlewareSpy.actionSpyMiddleware, + ] + ); + const MockKibanaContextProvider = createKibanaContextProviderMock(); const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => ( diff --git a/x-pack/plugins/security_solution/public/common/mock/index.ts b/x-pack/plugins/security_solution/public/common/mock/index.ts index bdad0ab1712abe..7e076772c42fb1 100644 --- a/x-pack/plugins/security_solution/public/common/mock/index.ts +++ b/x-pack/plugins/security_solution/public/common/mock/index.ts @@ -10,6 +10,7 @@ export * from './hook_wrapper'; export * from './index_pattern'; export * from './mock_timeline_data'; export * from './mock_detail_item'; +export * from './mock_local_storage'; export * from './netflow'; export * from './test_providers'; export * from './utils'; diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index 679e0bdc14cd5a..ce64d29d58fcab 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -22,6 +22,7 @@ import { mockGlobalState } from './global_state'; import { createKibanaContextProviderMock } from './kibana_react'; import { FieldHook, useForm } from '../../shared_imports'; import { SUB_PLUGINS_REDUCER } from './utils'; +import { createSiemLocalStorageMock } from './mock_local_storage'; const state: State = mockGlobalState; @@ -59,11 +60,12 @@ Object.defineProperty(window, 'localStorage', { }); const MockKibanaContextProvider = createKibanaContextProviderMock(); +const siemLocalStorageMock = createSiemLocalStorageMock(); /** A utility for wrapping children in the providers required to run most tests */ const TestProvidersComponent: React.FC = ({ children, - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable), + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock), onDragEnd = jest.fn(), }) => ( @@ -83,7 +85,7 @@ export const TestProviders = React.memo(TestProvidersComponent); const TestProviderWithoutDragAndDropComponent: React.FC = ({ children, - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable), + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock), }) => ( {children} diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx b/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx index f52d854d986ff9..6d4046673f8918 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx @@ -15,7 +15,12 @@ import { AlertIndex } from '../index'; import { RouteCapture } from '../../../common/components/endpoint/route_capture'; import { depsStartMock } from '../../../common/mock/endpoint'; import { createStore } from '../../../common/store'; -import { SUB_PLUGINS_REDUCER, mockGlobalState, apolloClientObservable } from '../../../common/mock'; +import { + SUB_PLUGINS_REDUCER, + mockGlobalState, + apolloClientObservable, + createSiemLocalStorageMock, +} from '../../../common/mock'; export const alertPageTestRender = () => { /** @@ -25,7 +30,13 @@ export const alertPageTestRender = () => { /** * Create a store, with the middleware disabled. We don't want side effects being created by our code in this test. */ - const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + const store = createStore( + mockGlobalState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock + ); const depsStart = depsStartMock(); depsStart.data.ui.SearchBar.mockImplementation(() =>
); diff --git a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx index a38b25661cd5e1..375b50061fd989 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx @@ -9,7 +9,12 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Provider as ReduxStoreProvider } from 'react-redux'; -import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER } from '../../../common/mock'; +import { + apolloClientObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, +} from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { hostsModel } from '../../store'; import { mockData } from './mock'; @@ -20,10 +25,11 @@ describe('Authentication Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx index 45779bf37c77f3..a5f12465a277e1 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx @@ -15,6 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -35,12 +36,13 @@ jest.mock('../../../common/components/query_bar', () => ({ describe('Hosts Table', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; + const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx index d2ccbd76fac10a..d71922564b9eec 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx @@ -19,6 +19,7 @@ import { TestProviders, mockGlobalState, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../common/mock'; import { SiemNavigation } from '../../common/components/navigation'; import { inputsActions } from '../../common/store/inputs'; @@ -171,7 +172,13 @@ describe('Hosts - rendering', () => { ]; localSource[0].result.data.source.status.indicesExist = true; const myState: State = mockGlobalState; - const myStore = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + const myStore = createStore( + myState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock + ); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx index bce811c58e4367..c48c33ba9cc408 100644 --- a/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx @@ -14,6 +14,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { networkModel } from '../../store'; @@ -26,10 +27,11 @@ import { NarrowDateRange } from '../../../common/components/ml/types'; describe('IP Overview Component', () => { const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx index 70c952b1107451..df0584d664bdfe 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx @@ -8,7 +8,12 @@ import { shallow } from 'enzyme'; import React from 'react'; import { Provider as ReduxStoreProvider } from 'react-redux'; -import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER } from '../../../common/mock'; +import { + apolloClientObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, +} from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { KpiNetworkComponent } from '.'; import { mockData } from './mock'; @@ -19,10 +24,11 @@ describe('KpiNetwork Component', () => { const to = new Date('2019-06-18T06:00:00.000Z').valueOf(); const narrowDateRange = jest.fn(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx index 25449214b6e773..420948d14faf4d 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx @@ -15,6 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { State, createStore } from '../../../common/store'; import { networkModel } from '../../store'; @@ -26,11 +27,12 @@ import { mockData } from './mock'; describe('NetworkTopNFlow Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx index e9020421a411e6..0aacc9c43fef6c 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx @@ -15,6 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -27,11 +28,12 @@ describe('NetworkHttp Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx index 8552d3184fcc2d..2572dba2d4f960 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx @@ -17,6 +17,7 @@ import { mockIndexPattern, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -30,10 +31,11 @@ describe('NetworkTopCountries Table Component', () => { const state: State = mockGlobalState; const mount = useMountAppended(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx index e40bbd40f4cd28..f022b560be3ede 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx @@ -16,6 +16,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -27,11 +28,12 @@ describe('NetworkTopNFlow Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx index ffb68f4df82029..f26ca5eaaa3eb0 100644 --- a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx @@ -15,6 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -26,11 +27,12 @@ describe('Tls Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('Rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx index 981e182154c5e7..834407fd585573 100644 --- a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx @@ -16,6 +16,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -28,11 +29,12 @@ describe('Users Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); describe('Rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx index 22a5d7af88eb89..64f0b7c1d53461 100644 --- a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx @@ -20,6 +20,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -118,10 +119,11 @@ describe('Ip Details', () => { }); const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); localSource = cloneDeep(mocksSource); }); diff --git a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx index 23948209fccfe7..36fcb23aa4518d 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx @@ -18,6 +18,7 @@ import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../common/mock'; import { State, createStore } from '../../common/store'; import { inputsActions } from '../../common/store/inputs'; @@ -155,7 +156,13 @@ describe('rendering - rendering', () => { ]; localSource[0].result.data.source.status.indicesExist = true; const myState: State = mockGlobalState; - const myStore = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + const myStore = createStore( + myState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + siemLocalStorageMock + ); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx index 49347ab8105479..c1c4bbc1e346dd 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx @@ -14,6 +14,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { OverviewHost } from '.'; @@ -92,11 +93,12 @@ const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ describe('OverviewHost', () => { const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); test('it renders the expected widget title', () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx index 4451135c608ced..a265567dd87795 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx @@ -13,6 +13,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { OverviewNetwork } from '.'; @@ -83,11 +84,12 @@ const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ describe('OverviewNetwork', () => { const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); }); test('it renders the expected widget title', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx index d86c20d1e44504..2c75f53bac1207 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx @@ -14,6 +14,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { mockDataProviders } from '../timeline/data_providers/mock/mock_data_providers'; @@ -31,6 +32,7 @@ const usersViewing = ['elastic']; describe('Flyout', () => { const state: State = mockGlobalState; + const siemLocalStorageMock = createSiemLocalStorageMock(); describe('rendering', () => { test('it renders correctly against snapshot', () => { @@ -59,7 +61,8 @@ describe('Flyout', () => { const storeShowIsTrue = createStore( stateShowIsTrue, SUB_PLUGINS_REDUCER, - apolloClientObservable + apolloClientObservable, + siemLocalStorageMock ); const wrapper = mount( @@ -82,7 +85,8 @@ describe('Flyout', () => { const storeWithDataProviders = createStore( stateWithDataProviders, SUB_PLUGINS_REDUCER, - apolloClientObservable + apolloClientObservable, + siemLocalStorageMock ); const wrapper = mount( @@ -103,7 +107,8 @@ describe('Flyout', () => { const storeWithDataProviders = createStore( stateWithDataProviders, SUB_PLUGINS_REDUCER, - apolloClientObservable + apolloClientObservable, + siemLocalStorageMock ); const wrapper = mount( @@ -136,7 +141,8 @@ describe('Flyout', () => { const storeWithDataProviders = createStore( stateWithDataProviders, SUB_PLUGINS_REDUCER, - apolloClientObservable + apolloClientObservable, + siemLocalStorageMock ); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx index 8bdec78ec8da2a..37be820651e964 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx @@ -13,6 +13,7 @@ import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER, + createSiemLocalStorageMock, } from '../../../../common/mock'; import { createStore, State } from '../../../../common/store'; import { useThrottledResizeObserver } from '../../../../common/components/utils'; @@ -48,11 +49,12 @@ describe('Properties', () => { const usersViewing = ['elastic']; const state: State = mockGlobalState; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); beforeEach(() => { jest.clearAllMocks(); - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); mockedWidth = 1000; }); diff --git a/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts b/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts new file mode 100644 index 00000000000000..1e4e180de7da1f --- /dev/null +++ b/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts @@ -0,0 +1,10 @@ +/* + * 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. + */ + +export const createSiemLocalStorageMock = () => ({ + getAllTimelines: jest.fn(), + addTimeline: jest.fn(), +}); From 67a8815b3f8ecd6c05312b18c06fa1c02c3f90f5 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 2 Jun 2020 18:00:28 +0300 Subject: [PATCH 21/42] Add cypress test --- .../cypress/integration/events_viewer.spec.ts | 2 +- .../cypress/screens/hosts/events.ts | 3 - .../cypress/screens/hosts/main.ts | 2 + .../cypress/screens/timeline.ts | 20 +++++ .../cypress/tasks/hosts/events.ts | 5 -- .../cypress/tasks/hosts/main.ts | 3 + .../cypress/tasks/timeline.ts | 37 ++++++++ .../footer/__snapshots__/index.test.tsx.snap | 4 + .../components/timeline/footer/index.tsx | 2 + .../timeline_local_storage.spec.ts | 90 +++++++++++++++++++ .../cypress/screens/hosts/external_events.ts | 11 +++ 11 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts create mode 100644 x-pack/plugins/siem/cypress/screens/hosts/external_events.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts index 82b4f4f0fbe342..17d7c3ac1ef2a7 100644 --- a/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/events_viewer.spec.ts @@ -29,12 +29,12 @@ import { dragAndDropColumn, openEventsViewerFieldsBrowser, opensInspectQueryModal, - resetFields, waitsForEventsToBeLoaded, } from '../tasks/hosts/events'; import { clearSearchBar, kqlSearch } from '../tasks/siem_header'; import { HOSTS_PAGE } from '../urls/navigation'; +import { resetFields } from '../tasks/timeline'; const defaultHeadersInDefaultEcsCategory = [ { id: '@timestamp' }, diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts index ed46a90c872c8f..a946fefe273e10 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/events.ts @@ -36,7 +36,4 @@ export const LOCAL_EVENTS_COUNT = export const LOAD_MORE = '[data-test-subj="events-viewer-panel"] [data-test-subj="TimelineMoreButton"'; -export const RESET_FIELDS = - '[data-test-subj="events-viewer-panel"] [data-test-subj="reset-fields"]'; - export const SERVER_SIDE_EVENT_COUNT = '[data-test-subj="server-side-event-count"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts index 3c30562680e86d..585ba561d21236 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts @@ -12,6 +12,8 @@ export const AUTHENTICATIONS_TAB = '[data-test-subj="navigation-authentications" export const EVENTS_TAB = '[data-test-subj="navigation-events"]'; +export const EXTERNAL_ALERTS_TAB = '[data-test-subj="navigation-alerts"]'; + export const KQL_SEARCH_BAR = '[data-test-subj="queryInput"]'; export const UNCOMMON_PROCESSES_TAB = '[data-test-subj="navigation-uncommonProcesses"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index bb232b752994ae..e53e22d906ad0c 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -8,19 +8,39 @@ export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]'; export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]'; +export const COLUMNS = '[data-test-subj="data-driven-columns"]'; + export const DRAGGABLE_HEADER = '[data-test-subj="headers-group"] [data-test-subj="draggable-header"]'; +export const FIELDS_MENU = '[data-test-subj="show-field-browser-gear"]'; + export const HEADERS_GROUP = '[data-test-subj="headers-group"]'; +export const HEADER_SORT_BUTTON = '[data-test-subj="header-sort-button"]'; + export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]'; export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]'; export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]'; +export const ITEMS_PER_PAGE = '[data-test-subj="local-events-count"]'; + +export const ITEMS_PER_PAGE_BUTTON = '[data-test-subj="local-events-count-button"]'; + +export const EVENTS_PER_PAGE_BUTTON = (itemsPerPage: number) => + `[data-test-subj="items-per-page-option-${itemsPerPage}"]`; + export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]'; +export const REMOVE_COLUMN = '[data-test-subj="remove-column"]'; + +export const RESET_FIELDS = + '[data-test-subj="events-viewer-panel"] [data-test-subj="reset-fields"]'; + +export const ROWS = '[data-test-subj="event"]'; + export const SEARCH_OR_FILTER_CONTAINER = '[data-test-subj="timeline-search-or-filter-search-container"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts index a5936509892596..57c819d9678835 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/events.ts @@ -13,7 +13,6 @@ import { HOST_GEO_COUNTRY_NAME_CHECKBOX, INSPECT_QUERY, LOAD_MORE, - RESET_FIELDS, SERVER_SIDE_EVENT_COUNT, } from '../../screens/hosts/events'; import { DRAGGABLE_HEADER } from '../../screens/timeline'; @@ -53,10 +52,6 @@ export const opensInspectQueryModal = () => { .click({ force: true }); }; -export const resetFields = () => { - cy.get(RESET_FIELDS).click({ force: true }); -}; - export const waitsForEventsToBeLoaded = () => { cy.get(SERVER_SIDE_EVENT_COUNT).should('exist').invoke('text').should('not.equal', '0'); }; diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts index eba0c353fbf37c..7f8c960a74b25a 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts @@ -9,6 +9,7 @@ import { AUTHENTICATIONS_TAB, EVENTS_TAB, UNCOMMON_PROCESSES_TAB, + EXTERNAL_ALERTS_TAB, } from '../../screens/hosts/main'; export const openAllHosts = () => cy.get(ALL_HOSTS_TAB).click({ force: true }); @@ -17,4 +18,6 @@ export const openAuthentications = () => cy.get(AUTHENTICATIONS_TAB).click({ for export const openEvents = () => cy.get(EVENTS_TAB).click({ force: true }); +export const openExternalAlerts = () => cy.get(EXTERNAL_ALERTS_TAB).click({ force: true }); + export const openUncommonProcesses = () => cy.get(UNCOMMON_PROCESSES_TAB).click({ force: true }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 38da611428b2ef..97056dda83b0a7 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -21,9 +21,17 @@ import { TIMELINE_TITLE, TIMESTAMP_TOGGLE_FIELD, TOGGLE_TIMELINE_EXPAND_EVENT, + DRAGGABLE_HEADER, + REMOVE_COLUMN, + FIELDS_MENU, + RESET_FIELDS, + ITEMS_PER_PAGE_BUTTON, + HEADER_SORT_BUTTON, + EVENTS_PER_PAGE_BUTTON, } from '../screens/timeline'; import { drag, drop } from '../tasks/common'; +import { FIELDS_MENU_CATEGORY_ID, FIELDS_MENU_ID } from '../screens/hosts/external_events'; export const hostExistsQuery = 'host.name: *'; @@ -101,3 +109,32 @@ export const dragAndDropIdToggleFieldToTimeline = () => { drop(headersDropArea) ); }; + +export const removeColumn = (column: number) => { + cy.get(REMOVE_COLUMN).first().should('exist'); + cy.get(REMOVE_COLUMN).eq(column).click({ force: true }); + cy.wait(3000); // wait for DOM updates +}; + +export const addIdColumn = () => { + cy.get(FIELDS_MENU).click({ force: true }); + cy.get(FIELDS_MENU_CATEGORY_ID).eq(0).click({ force: true }); + cy.get(FIELDS_MENU_ID).check(); + cy.get(DRAGGABLE_HEADER).eq(1).invoke('text').should('eql', '_id'); + cy.wait(3000); // wait for DOM updates +}; + +export const resetFields = () => { + cy.get(RESET_FIELDS).click({ force: true }); +}; + +export const changeItemsPerPage = (itemsPerPage: number) => { + cy.get(ITEMS_PER_PAGE_BUTTON).click({ force: true }); + cy.get(EVENTS_PER_PAGE_BUTTON(itemsPerPage)).click(); + cy.wait(3000); // wait for DOM updates +}; + +export const sortByColumn = (column: number) => { + cy.get(HEADER_SORT_BUTTON).first().should('exist'); + cy.get(HEADER_SORT_BUTTON).eq(column).click({ force: true }); +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap index fcf0f1019e67ac..f155b379227f0e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap @@ -33,24 +33,28 @@ exports[`Footer Timeline Component rendering it renders the default timeline foo items={ Array [ 1 rows , 5 rows , 10 rows , diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx index 607e004a8294ea..08ca627a338b4a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx @@ -144,6 +144,7 @@ export const EventsCountComponent = ({ iconType="arrowDown" iconSide="right" onClick={onClick} + data-test-subj="local-events-count-button" /> {` ${i18n.OF} `} @@ -288,6 +289,7 @@ export const FooterComponent = ({ { closePopover(); onChangeItemsPerPage(item); diff --git a/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts new file mode 100644 index 00000000000000..54071711eddb54 --- /dev/null +++ b/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts @@ -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 { loginAndWaitForPage } from '../tasks/login'; +import { HOSTS_PAGE } from '../urls/navigation'; +import { openEvents } from '../tasks/hosts/main'; +import { DRAGGABLE_HEADER, ITEMS_PER_PAGE, ROWS, COLUMNS } from '../screens/timeline'; +import { + TABLE_COLUMN_EVENTS_MESSAGE, + TABLE_COLUMN_EVENTS_HOSTNAME, +} from '../screens/hosts/external_events'; +import { waitsForEventsToBeLoaded, openEventsViewerFieldsBrowser } from '../tasks/hosts/events'; +import { + removeColumn, + addIdColumn, + resetFields, + sortByColumn, + changeItemsPerPage, +} from '../tasks/timeline'; + +describe('persistent timeline', () => { + before(() => { + loginAndWaitForPage(HOSTS_PAGE); + openEvents(); + waitsForEventsToBeLoaded(); + }); + + afterEach(() => { + openEventsViewerFieldsBrowser(); + resetFields(); + }); + + it('persist the deletion of a column', () => { + cy.get(DRAGGABLE_HEADER) + .eq(TABLE_COLUMN_EVENTS_MESSAGE) + .invoke('text') + .should('equal', 'message'); + removeColumn(TABLE_COLUMN_EVENTS_MESSAGE); + cy.reload(); + cy.get(DRAGGABLE_HEADER).each(($el) => { + expect($el.text()).not.equal('message'); + }); + }); + + it('persist the addition of a column', () => { + addIdColumn(); + cy.reload(); + cy.get(DRAGGABLE_HEADER).eq(1).invoke('text').should('equal', '_id'); + }); + + it('persist when resetting the fields', () => { + removeColumn(TABLE_COLUMN_EVENTS_MESSAGE); + cy.get(DRAGGABLE_HEADER).should('have.length', 8); + openEventsViewerFieldsBrowser(); + resetFields(); + cy.wait(3000); + cy.reload(); + cy.get(DRAGGABLE_HEADER).then(($items) => { + expect($items).to.have.length(9); + expect($items).to.contain('message'); + }); + }); + + it('persist the total items per page', () => { + changeItemsPerPage(50); + cy.reload(); + cy.get(ITEMS_PER_PAGE).invoke('text').should('eql', '50'); + }); + + it('persist sorting', () => { + changeItemsPerPage(10); + waitsForEventsToBeLoaded(); + sortByColumn(TABLE_COLUMN_EVENTS_HOSTNAME - 1); + cy.wait(3000); + cy.reload(); + waitsForEventsToBeLoaded(); + + cy.get(ROWS).each(($el) => { + cy.wrap($el) + .find(COLUMNS) + .children() + .eq(TABLE_COLUMN_EVENTS_HOSTNAME) + .invoke('text') + .should('equal', 'suricata-iowa'); + }); + }); +}); diff --git a/x-pack/plugins/siem/cypress/screens/hosts/external_events.ts b/x-pack/plugins/siem/cypress/screens/hosts/external_events.ts new file mode 100644 index 00000000000000..c337baea2eda9b --- /dev/null +++ b/x-pack/plugins/siem/cypress/screens/hosts/external_events.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +export const TABLE_COLUMN_EVENTS_MESSAGE = 1; +export const TABLE_COLUMN_EVENTS_HOSTNAME = 2; + +export const FIELDS_MENU_CATEGORY_ID = '[data-test-subj="_id-category-count"]'; +export const FIELDS_MENU_ID = '[data-test-subj="field-_id-checkbox"]'; From c6a99946b064f101b4e8fdb587bc8a97f45eff7f Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 3 Jun 2020 13:28:14 +0300 Subject: [PATCH 22/42] Test siem local storage --- .../security_solution/public/app/app.tsx | 2 +- .../public/common/mock/test_providers.tsx | 18 +------ .../common/lib/local_storage/index.test.ts | 48 +++++++++++++++++++ .../public/common/lib/local_storage/index.tsx | 8 ++-- .../public/common/mock/mock_local_storage.ts | 21 ++++++++ 5 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 x-pack/plugins/siem/public/common/lib/local_storage/index.test.ts diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 771ebb2ab321a9..155964be965509 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -80,7 +80,7 @@ const StartAppComponent: FC = ({ subPlugins, ...libs }) => { const { i18n } = useKibana().services; const history = createHashHistory(); const libs$ = new BehaviorSubject(libs); - const storage = createSiemLocalStorage(); + const storage = createSiemLocalStorage(localStorage); const store = createStore( createInitialState({ diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index ce64d29d58fcab..bf261c8dbf104c 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -22,7 +22,7 @@ import { mockGlobalState } from './global_state'; import { createKibanaContextProviderMock } from './kibana_react'; import { FieldHook, useForm } from '../../shared_imports'; import { SUB_PLUGINS_REDUCER } from './utils'; -import { createSiemLocalStorageMock } from './mock_local_storage'; +import { createSiemLocalStorageMock, localStorageMock } from './mock_local_storage'; const state: State = mockGlobalState; @@ -39,22 +39,6 @@ export const apolloClient = new ApolloClient({ export const apolloClientObservable = new BehaviorSubject(apolloClient); -const localStorageMock = () => { - let store: Record = {}; - - return { - getItem: (key: string) => { - return store[key] || null; - }, - setItem: (key: string, value: unknown) => { - store[key] = value; - }, - clear() { - store = {}; - }, - }; -}; - Object.defineProperty(window, 'localStorage', { value: localStorageMock(), }); diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/index.test.ts b/x-pack/plugins/siem/public/common/lib/local_storage/index.test.ts new file mode 100644 index 00000000000000..b783cc11f11063 --- /dev/null +++ b/x-pack/plugins/siem/public/common/lib/local_storage/index.test.ts @@ -0,0 +1,48 @@ +/* + * 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 { createSiemLocalStorage, LOCAL_STORAGE_TIMELINE_KEY } from './'; +import { localStorageMock } from '../../mock'; + +describe('SiemLocalStorage', () => { + const localStorage = localStorageMock(); + const storage = createSiemLocalStorage(localStorage); + + beforeEach(() => { + localStorage.clear(); + }); + + it('adds a timeline when storage is empty', () => { + storage.addTimeline('timeline-1', {}); + expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ + 'timeline-1': {}, + }); + }); + + it('adds a timeline when storage contains another timelines', () => { + storage.addTimeline('timeline-1', {}); + storage.addTimeline('timeline-2', {}); + expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ + 'timeline-1': {}, + 'timeline-2': {}, + }); + }); + + it('gets all timelines correctly', () => { + storage.addTimeline('timeline-1', {}); + storage.addTimeline('timeline-2', {}); + const timelines = storage.getAllTimelines(); + expect(timelines).toEqual({ + 'timeline-1': {}, + 'timeline-2': {}, + }); + }); + + it('it gets null if there is no timelines', () => { + const timelines = storage.getAllTimelines(); + expect(timelines).toBe(null); + }); +}); diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx b/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx index 41349130857601..663410ddfc9920 100644 --- a/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx +++ b/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; +import { Storage, IStorage } from '../../../../../../../src/plugins/kibana_utils/public'; import { SiemStorage } from './types'; -const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; +export const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; -export const createSiemLocalStorage = (): SiemStorage => { - const storage = new Storage(localStorage); +export const createSiemLocalStorage = (store: IStorage): SiemStorage => { + const storage = new Storage(store); const getAllTimelines: SiemStorage['getAllTimelines'] = () => { return storage.get(LOCAL_STORAGE_TIMELINE_KEY); diff --git a/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts b/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts index 1e4e180de7da1f..5154cc9c76ca48 100644 --- a/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts +++ b/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts @@ -4,6 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ +import { IStorage } from '../../../../../../src/plugins/kibana_utils/public'; + +export const localStorageMock = (): IStorage => { + let store: Record = {}; + + return { + getItem: (key: string) => { + return store[key] || null; + }, + setItem: (key: string, value: unknown) => { + store[key] = value; + }, + clear() { + store = {}; + }, + removeItem(key: string) { + delete store[key]; + }, + }; +}; + export const createSiemLocalStorageMock = () => ({ getAllTimelines: jest.fn(), addTimeline: jest.fn(), From 0298a7b02b400b299d6074d9ee18bb4142e2ee78 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 3 Jun 2020 16:47:34 +0300 Subject: [PATCH 23/42] Test epic --- .../timelines/store/timeline/reducer.ts | 18 +- .../timeline/epic_local_storage.test.tsx | 171 ++++++++++++++++++ .../store/timeline/epic_local_storage.ts | 23 ++- 3 files changed, 192 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.test.tsx diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index e9c203d7ac4381..c2e755593b4d5b 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -216,14 +216,16 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) ...state, timelineById: pinTimelineEvent({ id, eventId, timelineById: state.timelineById }), })) - .case(removeColumn, (state, { id, columnId }) => ({ - ...state, - timelineById: removeTimelineColumn({ - id, - columnId, - timelineById: state.timelineById, - }), - })) + .case(removeColumn, (state, { id, columnId }) => { + return { + ...state, + timelineById: removeTimelineColumn({ + id, + columnId, + timelineById: state.timelineById, + }), + }; + }) .case(removeProvider, (state, { id, providerId, andProviderId }) => ({ ...state, timelineById: removeTimelineProvider({ diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.test.tsx new file mode 100644 index 00000000000000..45b4c903d61258 --- /dev/null +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -0,0 +1,171 @@ +/* + * 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 from 'react'; +import { shallow } from 'enzyme'; + +import { + mockGlobalState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + TestProviders, + defaultHeaders, + createSiemLocalStorageMock, + mockIndexPattern, +} from '../../../common/mock'; + +import { createStore, State } from '../../../common/store'; +import { + removeColumn, + upsertColumn, + applyDeltaToColumnWidth, + updateColumns, + updateItemsPerPage, + updateSort, +} from './actions'; + +import { + TimelineComponent, + Props as TimelineComponentProps, +} from '../../components/timeline/timeline'; +import { mockBrowserFields } from '../../../common/containers/source/mock'; +import { mockDataProviders } from '../../components/timeline/data_providers/mock/mock_data_providers'; +import { Sort } from '../../components/timeline/body/sort'; +import { Direction } from '../../../graphql/types'; + +import { isPageTimeline } from './epic_local_storage'; + +const wait = (ms: number = 500): Promise => { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; + +describe('epicLocalStorage', () => { + const state: State = mockGlobalState; + const siemLocalStorageMock = createSiemLocalStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + + let props = {} as TimelineComponentProps; + const sort: Sort = { + columnId: '@timestamp', + sortDirection: Direction.desc, + }; + const startDate = new Date('2018-03-23T18:49:23.132Z').valueOf(); + const endDate = new Date('2018-03-24T03:33:52.253Z').valueOf(); + + const indexPattern = mockIndexPattern; + + beforeEach(() => { + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + props = { + browserFields: mockBrowserFields, + columns: defaultHeaders, + id: 'foo', + dataProviders: mockDataProviders, + end: endDate, + eventType: 'raw' as TimelineComponentProps['eventType'], + filters: [], + indexPattern, + indexToAdd: [], + isLive: false, + itemsPerPage: 5, + itemsPerPageOptions: [5, 10, 20], + kqlMode: 'search' as TimelineComponentProps['kqlMode'], + kqlQueryExpression: '', + loadingIndexName: false, + onChangeItemsPerPage: jest.fn(), + onClose: jest.fn(), + onDataProviderEdited: jest.fn(), + onDataProviderRemoved: jest.fn(), + onToggleDataProviderEnabled: jest.fn(), + onToggleDataProviderExcluded: jest.fn(), + show: true, + showCallOutUnauthorizedMsg: false, + start: startDate, + sort, + toggleColumn: jest.fn(), + usersViewing: ['elastic'], + }; + }); + + it('filters correctly page timelines', () => { + expect(isPageTimeline('timeline-1')).toBe(false); + expect(isPageTimeline('hosts-page-alerts')).toBe(true); + }); + + it('persist adding / reordering of a column correctly', async () => { + shallow( + + + + ); + store.dispatch(upsertColumn({ id: 'test', index: 1, column: defaultHeaders[0] })); + await wait(); + expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + }); + + it('persist timeline when removing a column ', async () => { + shallow( + + + + ); + store.dispatch(removeColumn({ id: 'test', columnId: '@timestamp' })); + await wait(); + expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + }); + + it('persists resizing of a column', async () => { + shallow( + + + + ); + store.dispatch(applyDeltaToColumnWidth({ id: 'test', columnId: '@timestamp', delta: 80 })); + await wait(); + expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + }); + + it('persist the resetting of the fields', async () => { + shallow( + + + + ); + store.dispatch(updateColumns({ id: 'test', columns: defaultHeaders })); + await wait(); + expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + }); + + it('persist items per page', async () => { + shallow( + + + + ); + store.dispatch(updateItemsPerPage({ id: 'test', itemsPerPage: 50 })); + await wait(); + expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + }); + + it('persist the sorting of a column', async () => { + shallow( + + + + ); + store.dispatch( + updateSort({ + id: 'test', + sort: { + columnId: 'event.severity', + sortDirection: Direction.desc, + }, + }) + ); + await wait(); + expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts index fd0a78db8c1efa..4bfc6ea3909656 100644 --- a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts @@ -5,7 +5,6 @@ */ import { Action } from 'redux'; -import { merge } from 'rxjs'; import { map, filter, ignoreElements, tap, withLatestFrom, delay } from 'rxjs/operators'; import { Epic } from 'redux-observable'; import { get } from 'lodash/fp'; @@ -30,7 +29,7 @@ const timelineActionTypes = [ updateSort.type, ]; -const isPageTimeline = (timelineId: string | undefined): boolean => +export const isPageTimeline = (timelineId: string | undefined): boolean => // Is not a flyout timeline !(timelineId && timelineId.toLowerCase().startsWith('timeline')); @@ -42,18 +41,18 @@ export const createTimelineLocalStorageEpic = (): Epic< > => (action$, state$, { timelineByIdSelector, storage }) => { const timeline$ = state$.pipe(map(timelineByIdSelector), filter(isNotNull)); - return merge( - action$.pipe( - delay(500), - withLatestFrom(timeline$), - filter(([action]) => isPageTimeline(get('payload.id', action))), - tap(([action, timelineById]) => { - if (timelineActionTypes.includes(action.type)) { + return action$.pipe( + delay(500), + withLatestFrom(timeline$), + filter(([action]) => isPageTimeline(get('payload.id', action))), + tap(([action, timelineById]) => { + if (timelineActionTypes.includes(action.type)) { + if (storage && storage.addTimeline) { const timelineId: string = get('payload.id', action); storage.addTimeline(timelineId, timelineById[timelineId]); } - }), - ignoreElements() - ) + } + }), + ignoreElements() ); }; From c5f824842bfbe23769e8f76abac3557392103022 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 3 Jun 2020 19:50:06 +0300 Subject: [PATCH 24/42] Remove unecessary cypress tests --- .../timeline_local_storage.spec.ts | 58 +------------------ 1 file changed, 3 insertions(+), 55 deletions(-) diff --git a/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts index 54071711eddb54..87c2c004bbc3b6 100644 --- a/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts +++ b/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts @@ -7,19 +7,10 @@ import { loginAndWaitForPage } from '../tasks/login'; import { HOSTS_PAGE } from '../urls/navigation'; import { openEvents } from '../tasks/hosts/main'; -import { DRAGGABLE_HEADER, ITEMS_PER_PAGE, ROWS, COLUMNS } from '../screens/timeline'; -import { - TABLE_COLUMN_EVENTS_MESSAGE, - TABLE_COLUMN_EVENTS_HOSTNAME, -} from '../screens/hosts/external_events'; +import { DRAGGABLE_HEADER } from '../screens/timeline'; +import { TABLE_COLUMN_EVENTS_MESSAGE } from '../screens/hosts/external_events'; import { waitsForEventsToBeLoaded, openEventsViewerFieldsBrowser } from '../tasks/hosts/events'; -import { - removeColumn, - addIdColumn, - resetFields, - sortByColumn, - changeItemsPerPage, -} from '../tasks/timeline'; +import { removeColumn, resetFields } from '../tasks/timeline'; describe('persistent timeline', () => { before(() => { @@ -44,47 +35,4 @@ describe('persistent timeline', () => { expect($el.text()).not.equal('message'); }); }); - - it('persist the addition of a column', () => { - addIdColumn(); - cy.reload(); - cy.get(DRAGGABLE_HEADER).eq(1).invoke('text').should('equal', '_id'); - }); - - it('persist when resetting the fields', () => { - removeColumn(TABLE_COLUMN_EVENTS_MESSAGE); - cy.get(DRAGGABLE_HEADER).should('have.length', 8); - openEventsViewerFieldsBrowser(); - resetFields(); - cy.wait(3000); - cy.reload(); - cy.get(DRAGGABLE_HEADER).then(($items) => { - expect($items).to.have.length(9); - expect($items).to.contain('message'); - }); - }); - - it('persist the total items per page', () => { - changeItemsPerPage(50); - cy.reload(); - cy.get(ITEMS_PER_PAGE).invoke('text').should('eql', '50'); - }); - - it('persist sorting', () => { - changeItemsPerPage(10); - waitsForEventsToBeLoaded(); - sortByColumn(TABLE_COLUMN_EVENTS_HOSTNAME - 1); - cy.wait(3000); - cy.reload(); - waitsForEventsToBeLoaded(); - - cy.get(ROWS).each(($el) => { - cy.wrap($el) - .find(COLUMNS) - .children() - .eq(TABLE_COLUMN_EVENTS_HOSTNAME) - .invoke('text') - .should('equal', 'suricata-iowa'); - }); - }); }); From 85392d8cd0a208cbe910803ebaaaa02697d78554 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 4 Jun 2020 11:26:47 +0300 Subject: [PATCH 25/42] Fix detections page timelines --- .../components/alerts_table/index.test.tsx | 1 + .../alerts/components/alerts_table/index.tsx | 28 +++++++++---------- .../detection_engine/detection_engine.tsx | 4 +-- .../detection_engine/rules/details/index.tsx | 3 ++ .../security_solution/public/app/app.tsx | 5 ++-- .../public/timelines/containers/index.tsx | 7 +++-- .../timelines/store/timeline/helpers.ts | 13 +-------- .../timelines/store/timeline/reducer.ts | 20 ++++++------- 8 files changed, 38 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx index 51fdd828bcddb3..7f7c74e9b036ea 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx @@ -13,6 +13,7 @@ describe('AlertsTableComponent', () => { it('renders correctly', () => { const wrapper = shallow( = ({ + timelineId, canUserCRUD, clearEventsDeleted, clearEventsLoading, @@ -141,16 +141,16 @@ export const AlertsTableComponent: React.FC = ({ const setEventsLoadingCallback = useCallback( ({ eventIds, isLoading }: SetEventsLoadingProps) => { - setEventsLoading!({ id: ALERTS_TABLE_TIMELINE_ID, eventIds, isLoading }); + setEventsLoading!({ id: timelineId, eventIds, isLoading }); }, - [setEventsLoading, ALERTS_TABLE_TIMELINE_ID] + [setEventsLoading, timelineId] ); const setEventsDeletedCallback = useCallback( ({ eventIds, isDeleted }: SetEventsDeletedProps) => { - setEventsDeleted!({ id: ALERTS_TABLE_TIMELINE_ID, eventIds, isDeleted }); + setEventsDeleted!({ id: timelineId, eventIds, isDeleted }); }, - [setEventsDeleted, ALERTS_TABLE_TIMELINE_ID] + [setEventsDeleted, timelineId] ); const onAlertStatusUpdateSuccess = useCallback( @@ -186,9 +186,9 @@ export const AlertsTableComponent: React.FC = ({ // Callback for when open/closed filter changes const onFilterGroupChangedCallback = useCallback( (newFilterGroup: AlertFilterOption) => { - clearEventsLoading!({ id: ALERTS_TABLE_TIMELINE_ID }); - clearEventsDeleted!({ id: ALERTS_TABLE_TIMELINE_ID }); - clearSelected!({ id: ALERTS_TABLE_TIMELINE_ID }); + clearEventsLoading!({ id: timelineId }); + clearEventsDeleted!({ id: timelineId }); + clearSelected!({ id: timelineId }); setFilterGroup(newFilterGroup); }, [clearEventsLoading, clearEventsDeleted, clearSelected, setFilterGroup] @@ -196,7 +196,7 @@ export const AlertsTableComponent: React.FC = ({ // Callback for clearing entire selection from utility bar const clearSelectionCallback = useCallback(() => { - clearSelected!({ id: ALERTS_TABLE_TIMELINE_ID }); + clearSelected!({ id: timelineId }); setSelectAll(false); setShowClearSelectionAction(false); }, [clearSelected, setSelectAll, setShowClearSelectionAction]); @@ -343,7 +343,7 @@ export const AlertsTableComponent: React.FC = ({ defaultModel={alertsDefaultModel} end={to} headerFilterGroup={headerFilterGroup} - id={ALERTS_TABLE_TIMELINE_ID} + id={timelineId} start={from} utilityBar={utilityBarCallback} /> @@ -353,9 +353,9 @@ export const AlertsTableComponent: React.FC = ({ const makeMapStateToProps = () => { const getTimeline = timelineSelectors.getTimelineByIdSelector(); const getGlobalInputs = inputsSelectors.globalSelector(); - const mapStateToProps = (state: State) => { - const timeline: TimelineModel = - getTimeline(state, ALERTS_TABLE_TIMELINE_ID) ?? timelineDefaults; + const mapStateToProps = (state: State, ownProps: OwnProps) => { + const { timelineId } = ownProps; + const timeline: TimelineModel = getTimeline(state, timelineId) ?? timelineDefaults; const { deletedEventIds, isSelectAllChecked, loadingEventIds, selectedEventIds } = timeline; const globalInputs: inputsModel.InputsRange = getGlobalInputs(state); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx index 7517f6cebc6b4c..4808ccb794dfb6 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx @@ -37,7 +37,7 @@ import { DetectionEngineHeaderPage } from '../../components/detection_engine_hea import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; -const ALERTS_TABLE_ID = 'alerts-page-alerts'; +export const ALERTS_TABLE_ID = 'detections-page-alerts'; export const DetectionEnginePageComponent: React.FC = ({ filters, @@ -140,7 +140,7 @@ export const DetectionEnginePageComponent: React.FC = ({ /> = ({ {ruleId != null && ( = ({ subPlugins, ...libs }) => { createInitialState({ ...subPluginsStore.initialState, timeline: { - ...subPluginsStore.initialState.timeline, + ...(subPluginsStore.initialState.timeline ?? initialTimelineState), timelineById: { - ...subPluginsStore.initialState.timeline.timelineById, + ...(subPluginsStore.initialState.timeline?.timelineById ?? {}), ...(storage.getAllTimelines() ?? {}), }, }, diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 5efcb845391235..19356fd43946d7 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -27,7 +27,10 @@ import { QueryTemplate, QueryTemplateProps } from '../../common/containers/query import { EventType } from '../../timelines/store/timeline/model'; import { timelineQuery } from './index.gql_query'; import { timelineActions } from '../../timelines/store/timeline'; -import { ALERTS_TABLE_TIMELINE_ID } from '../../alerts/components/alerts_table'; +import { ALERTS_TABLE_ID as DETECTION_ENGINE_TIMELINE_ID } from '../../alerts/pages/detection_engine/rules/details'; +import { ALERTS_TABLE_ID as DETECTION_ENGINE_RULES_TIMELINE_ID } from '../../alerts/pages/detection_engine/detection_engine'; + +const timelineIds = [DETECTION_ENGINE_TIMELINE_ID, DETECTION_ENGINE_RULES_TIMELINE_ID]; export interface TimelineArgs { events: TimelineItem[]; @@ -182,7 +185,7 @@ const makeMapStateToProps = () => { const mapDispatchToProps = (dispatch: Dispatch) => ({ clearSignalsState: ({ id }: { id?: string }) => { - if (id != null && id === ALERTS_TABLE_TIMELINE_ID) { + if (id != null && timelineIds.some((timelineId) => timelineId === id)) { dispatch(timelineActions.clearEventsLoading({ id })); dispatch(timelineActions.clearEventsDeleted({ id })); } diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts index b2472cbe89a50c..97db792c2a551e 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts @@ -18,22 +18,11 @@ import { KueryFilterQuery, SerializedFilterQuery } from '../../../common/store/m import { timelineDefaults } from './defaults'; import { ColumnHeaderOptions, KqlMode, TimelineModel, EventType } from './model'; -import { TimelineById, TimelineState } from './types'; +import { TimelineById } from './types'; import { TimelineNonEcsData } from '../../../graphql/types'; -const EMPTY_TIMELINE_BY_ID: TimelineById = {}; // stable reference - export const isNotNull = (value: T | null): value is T => value !== null; -export const initialTimelineState: TimelineState = { - timelineById: EMPTY_TIMELINE_BY_ID, - autoSavedWarningMsg: { - timelineId: null, - newTimelineModel: null, - }, - showCallOutUnauthorizedMsg: false, -}; - interface AddTimelineHistoryParams { id: string; historyId: string; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index c2e755593b4d5b..a8ae39527cdbfc 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -100,7 +100,7 @@ import { import { TimelineState, EMPTY_TIMELINE_BY_ID } from './types'; export const initialTimelineState: TimelineState = { - timelineById: { ...EMPTY_TIMELINE_BY_ID }, + timelineById: EMPTY_TIMELINE_BY_ID, autoSavedWarningMsg: { timelineId: null, newTimelineModel: null, @@ -216,16 +216,14 @@ export const timelineReducer = reducerWithInitialState(initialTimelineState) ...state, timelineById: pinTimelineEvent({ id, eventId, timelineById: state.timelineById }), })) - .case(removeColumn, (state, { id, columnId }) => { - return { - ...state, - timelineById: removeTimelineColumn({ - id, - columnId, - timelineById: state.timelineById, - }), - }; - }) + .case(removeColumn, (state, { id, columnId }) => ({ + ...state, + timelineById: removeTimelineColumn({ + id, + columnId, + timelineById: state.timelineById, + }), + })) .case(removeProvider, (state, { id, providerId, andProviderId }) => ({ ...state, timelineById: removeTimelineProvider({ From d4e89d87c7b787a7a44bb83b2d65266a652a7272 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 4 Jun 2020 13:11:45 +0300 Subject: [PATCH 26/42] Fix rebase --- .../cypress/integration/timeline_local_storage.spec.ts | 0 .../cypress/screens/hosts/external_events.ts | 0 .../public/alerts/components/alerts_table/index.tsx | 4 ++-- .../public/alerts/pages/detection_engine/constants.ts | 8 ++++++++ .../alerts/pages/detection_engine/detection_engine.tsx | 3 +-- .../alerts/pages/detection_engine/rules/details/index.tsx | 5 ++--- .../common/components/alerts_viewer/alerts_table.tsx | 2 +- .../public/common/lib/local_storage/index.test.ts | 2 +- .../public/common/lib/local_storage/index.tsx | 0 .../public/common/lib/local_storage/types.ts | 0 .../public/common/mock/mock_local_storage.ts | 0 .../public/timelines/containers/index.tsx | 6 ++++-- .../timelines/store/timeline/epic_local_storage.test.tsx | 0 .../public/timelines/store/timeline/epic_local_storage.ts | 0 14 files changed, 19 insertions(+), 11 deletions(-) rename x-pack/plugins/{siem => security_solution}/cypress/integration/timeline_local_storage.spec.ts (100%) rename x-pack/plugins/{siem => security_solution}/cypress/screens/hosts/external_events.ts (100%) create mode 100644 x-pack/plugins/security_solution/public/alerts/pages/detection_engine/constants.ts rename x-pack/plugins/{siem => security_solution}/public/common/lib/local_storage/index.test.ts (99%) rename x-pack/plugins/{siem => security_solution}/public/common/lib/local_storage/index.tsx (100%) rename x-pack/plugins/{siem => security_solution}/public/common/lib/local_storage/types.ts (100%) rename x-pack/plugins/{siem => security_solution}/public/common/mock/mock_local_storage.ts (100%) rename x-pack/plugins/{siem => security_solution}/public/timelines/store/timeline/epic_local_storage.test.tsx (100%) rename x-pack/plugins/{siem => security_solution}/public/timelines/store/timeline/epic_local_storage.ts (100%) diff --git a/x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts similarity index 100% rename from x-pack/plugins/siem/cypress/integration/timeline_local_storage.spec.ts rename to x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts diff --git a/x-pack/plugins/siem/cypress/screens/hosts/external_events.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/external_events.ts similarity index 100% rename from x-pack/plugins/siem/cypress/screens/hosts/external_events.ts rename to x-pack/plugins/security_solution/cypress/screens/hosts/external_events.ts diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx index c7d5ac5ad09aa4..4247680741228a 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.tsx @@ -307,7 +307,7 @@ export const AlertsTableComponent: React.FC = ({ useEffect(() => { initializeTimeline({ - id: ALERTS_TABLE_TIMELINE_ID, + id: timelineId, documentType: i18n.ALERTS_DOCUMENT_TYPE, footerText: i18n.TOTAL_COUNT_OF_ALERTS, loadingText: i18n.LOADING_ALERTS, @@ -317,7 +317,7 @@ export const AlertsTableComponent: React.FC = ({ }, []); useEffect(() => { setTimelineRowActions({ - id: ALERTS_TABLE_TIMELINE_ID, + id: timelineId, queryFields: requiredFieldsForActions, timelineRowActions: additionalActions, }); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/constants.ts b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/constants.ts new file mode 100644 index 00000000000000..0225677d1f2488 --- /dev/null +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/constants.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export const SINGLE_RULE_ALERTS_TABLE_ID = 'detections-page-rule-details'; +export const ALERTS_TABLE_ID = 'detections-page-alerts'; diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx index 4808ccb794dfb6..4ac71912ec650c 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx @@ -36,8 +36,7 @@ import { DetectionEngineNoIndex } from './detection_engine_no_signal_index'; import { DetectionEngineHeaderPage } from '../../components/detection_engine_header_page'; import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; - -export const ALERTS_TABLE_ID = 'detections-page-alerts'; +import { ALERTS_TABLE_ID } from './constants'; export const DetectionEnginePageComponent: React.FC = ({ filters, diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx index 980b3a2fb27e98..55009be89d11fd 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx @@ -70,8 +70,7 @@ import { FailureHistory } from './failure_history'; import { RuleStatus } from '../../../../components/rules//rule_status'; import { useMlCapabilities } from '../../../../../common/components/ml_popover/hooks/use_ml_capabilities'; import { hasMlAdminPermissions } from '../../../../../../common/machine_learning/has_ml_admin_permissions'; - -export const ALERTS_TABLE_ID = 'detections-page-rule-details'; +import { SINGLE_RULE_ALERTS_TABLE_ID } from '../../constants'; enum RuleDetailTabs { alerts = 'alerts', @@ -376,7 +375,7 @@ export const RuleDetailsPageComponent: FC = ({ {ruleId != null && ( = ({ id, endDate, startDate, pageFil useEffect(() => { initializeTimeline({ - id: ALERTS_TABLE_ID, + id, documentType: i18n.ALERTS_DOCUMENT_TYPE, footerText: i18n.TOTAL_COUNT_OF_ALERTS, title: i18n.ALERTS_TABLE_TITLE, diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/index.test.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts similarity index 99% rename from x-pack/plugins/siem/public/common/lib/local_storage/index.test.ts rename to x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts index b783cc11f11063..6ed156941b798b 100644 --- a/x-pack/plugins/siem/public/common/lib/local_storage/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createSiemLocalStorage, LOCAL_STORAGE_TIMELINE_KEY } from './'; +import { createSiemLocalStorage, LOCAL_STORAGE_TIMELINE_KEY } from '.'; import { localStorageMock } from '../../mock'; describe('SiemLocalStorage', () => { diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/index.tsx b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx similarity index 100% rename from x-pack/plugins/siem/public/common/lib/local_storage/index.tsx rename to x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx diff --git a/x-pack/plugins/siem/public/common/lib/local_storage/types.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts similarity index 100% rename from x-pack/plugins/siem/public/common/lib/local_storage/types.ts rename to x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts diff --git a/x-pack/plugins/siem/public/common/mock/mock_local_storage.ts b/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts similarity index 100% rename from x-pack/plugins/siem/public/common/mock/mock_local_storage.ts rename to x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 19356fd43946d7..c99fa46b9e7944 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -27,8 +27,10 @@ import { QueryTemplate, QueryTemplateProps } from '../../common/containers/query import { EventType } from '../../timelines/store/timeline/model'; import { timelineQuery } from './index.gql_query'; import { timelineActions } from '../../timelines/store/timeline'; -import { ALERTS_TABLE_ID as DETECTION_ENGINE_TIMELINE_ID } from '../../alerts/pages/detection_engine/rules/details'; -import { ALERTS_TABLE_ID as DETECTION_ENGINE_RULES_TIMELINE_ID } from '../../alerts/pages/detection_engine/detection_engine'; +import { + ALERTS_TABLE_ID as DETECTION_ENGINE_TIMELINE_ID, + SINGLE_RULE_ALERTS_TABLE_ID as DETECTION_ENGINE_RULES_TIMELINE_ID, +} from '../../alerts/pages/detection_engine/constants'; const timelineIds = [DETECTION_ENGINE_TIMELINE_ID, DETECTION_ENGINE_RULES_TIMELINE_ID]; diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx similarity index 100% rename from x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.test.tsx rename to x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx diff --git a/x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts similarity index 100% rename from x-pack/plugins/siem/public/timelines/store/timeline/epic_local_storage.ts rename to x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts From 4a780b9502f523ff89ec2357aa8008e94fc1ba1a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 4 Jun 2020 18:21:14 +0300 Subject: [PATCH 27/42] Rename to new naming --- .../security_solution/public/app/app.tsx | 4 +-- .../index.test.tsx | 18 ++++++++--- .../error_toast_dispatcher/index.test.tsx | 18 ++++++++--- .../common/components/inspect/index.test.tsx | 15 ++++++---- .../components/stat_items/index.test.tsx | 6 ++-- .../super_date_picker/index.test.tsx | 13 +++++--- .../common/components/top_n/index.test.tsx | 11 +++++-- .../common/lib/local_storage/index.test.ts | 4 +-- .../public/common/lib/local_storage/index.tsx | 10 +++---- .../public/common/lib/local_storage/types.ts | 2 +- .../mock/endpoint/app_context_render.tsx | 6 ++-- .../public/common/mock/mock_local_storage.ts | 2 +- .../public/common/mock/test_providers.tsx | 18 ++++++++--- .../public/common/store/store.ts | 4 +-- .../view/test_helpers/render_alert_page.tsx | 6 ++-- .../authentications_table/index.test.tsx | 18 ++++++++--- .../components/hosts_table/index.test.tsx | 18 ++++++++--- .../public/hosts/pages/hosts.test.tsx | 6 ++-- .../components/ip_overview/index.test.tsx | 18 ++++++++--- .../components/kpi_network/index.test.tsx | 18 ++++++++--- .../network_dns_table/index.test.tsx | 18 ++++++++--- .../network_http_table/index.test.tsx | 18 ++++++++--- .../index.test.tsx | 18 ++++++++--- .../network_top_n_flow_table/index.test.tsx | 18 ++++++++--- .../components/tls_table/index.test.tsx | 18 ++++++++--- .../components/users_table/index.test.tsx | 18 ++++++++--- .../network/pages/ip_details/index.test.tsx | 18 ++++++++--- .../public/network/pages/network.test.tsx | 6 ++-- .../components/overview_host/index.test.tsx | 18 ++++++++--- .../overview_network/index.test.tsx | 18 ++++++++--- .../components/flyout/index.test.tsx | 12 ++++---- .../timeline/properties/index.test.tsx | 18 ++++++++--- .../timeline/epic_local_storage.test.tsx | 30 ++++++++++++------- .../public/timelines/store/timeline/types.ts | 4 +-- 34 files changed, 321 insertions(+), 126 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 6f4ff361b275ae..513c80cc593cb7 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -33,7 +33,7 @@ import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; import { ApolloClientContext } from '../common/utils/apollo_context'; import { SecuritySubPlugins } from './types'; -import { createSiemLocalStorage } from '../common/lib/local_storage'; +import { createSecuritySolutionStorage } from '../common/lib/local_storage'; import { initialTimelineState } from '../timelines/store/timeline/reducer'; interface AppPluginRootComponentProps { @@ -81,7 +81,7 @@ const StartAppComponent: FC = ({ subPlugins, ...libs }) => { const { i18n } = useKibana().services; const history = createHashHistory(); const libs$ = new BehaviorSubject(libs); - const storage = createSiemLocalStorage(localStorage); + const storage = createSecuritySolutionStorage(localStorage); const store = createStore( createInitialState({ diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx index 80c6fc243433b4..3bc824dc5c4c7e 100644 --- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx @@ -12,7 +12,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../mock'; import { createStore, State } from '../../store'; import { AddFilterToGlobalSearchBar } from '.'; @@ -34,11 +34,21 @@ jest.mock('../../lib/kibana', () => ({ describe('AddFilterToGlobalSearchBar Component', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); mockAddFilters.mockClear(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx index cca68d26287bb1..18d4b6da247108 100644 --- a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx @@ -12,7 +12,7 @@ import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../mock'; import { createStore } from '../../store/store'; @@ -21,11 +21,21 @@ import { State } from '../../store/types'; describe('Error Toast Dispatcher', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx index 659c58f76d0d1c..290d2af6255349 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx @@ -14,7 +14,7 @@ import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../mock'; import { createStore, State } from '../../store'; import { UpdateQueryParams, upsertQuery } from '../../store/inputs/helpers'; @@ -26,7 +26,7 @@ describe('Inspect Button', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); const refetch = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); const newQuery: UpdateQueryParams = { inputId: 'global', id: 'myQuery', @@ -36,7 +36,12 @@ describe('Inspect Button', () => { state: state.inputs, }; - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); describe('Render', () => { beforeEach(() => { @@ -46,7 +51,7 @@ describe('Inspect Button', () => { myState, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); }); test('Eui Empty Button', () => { @@ -155,7 +160,7 @@ describe('Inspect Button', () => { myState, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); }); test('Open Inspect Modal', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx index 9e7ff2cbb72bdb..48694fea717c46 100644 --- a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx @@ -34,7 +34,7 @@ import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../mock'; import { State, createStore } from '../../store'; import { Provider as ReduxStoreProvider } from 'react-redux'; @@ -54,12 +54,12 @@ jest.mock('../charts/barchart', () => { describe('Stat Items Component', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); const store = createStore( state, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); describe.each([ diff --git a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx index 703683e7e93fd9..bca3305c352ece 100644 --- a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx @@ -14,7 +14,7 @@ import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../mock'; import { createUseUiSetting$Mock } from '../../mock/kibana_react'; import { createStore, State } from '../../store'; @@ -80,17 +80,22 @@ const timepickerRanges = [ describe('SIEM Super Date Picker', () => { describe('#SuperDatePicker', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); let store = createStore( state, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); beforeEach(() => { jest.clearAllMocks(); - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); mockUseUiSetting$.mockImplementation((key, defaultValue) => { const useUiSetting$Mock = createUseUiSetting$Mock(); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index 08d48e10e6d80d..dc1958464382f4 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -13,7 +13,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../mock'; import { createKibanaCoreStartMock } from '../../mock/kibana_core'; import { FilterManager } from '../../../../../../../src/plugins/data/public'; @@ -143,8 +143,13 @@ const state: State = { }, }; -const siemLocalStorageMock = createSiemLocalStorageMock(); -const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); +const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); +const store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock +); describe('StatefulTopN', () => { // Suppress warnings about "react-beautiful-dnd" diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts index 6ed156941b798b..6900b766c53347 100644 --- a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createSiemLocalStorage, LOCAL_STORAGE_TIMELINE_KEY } from '.'; +import { createSecuritySolutionStorage, LOCAL_STORAGE_TIMELINE_KEY } from '.'; import { localStorageMock } from '../../mock'; describe('SiemLocalStorage', () => { const localStorage = localStorageMock(); - const storage = createSiemLocalStorage(localStorage); + const storage = createSecuritySolutionStorage(localStorage); beforeEach(() => { localStorage.clear(); diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx index 663410ddfc9920..2941281de2fa22 100644 --- a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx @@ -5,18 +5,18 @@ */ import { Storage, IStorage } from '../../../../../../../src/plugins/kibana_utils/public'; -import { SiemStorage } from './types'; +import { SecuritySolutionStorage } from './types'; export const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; -export const createSiemLocalStorage = (store: IStorage): SiemStorage => { +export const createSecuritySolutionStorage = (store: IStorage): SecuritySolutionStorage => { const storage = new Storage(store); - const getAllTimelines: SiemStorage['getAllTimelines'] = () => { + const getAllTimelines: SecuritySolutionStorage['getAllTimelines'] = () => { return storage.get(LOCAL_STORAGE_TIMELINE_KEY); }; - const addTimeline: SiemStorage['addTimeline'] = (id, timeline) => { + const addTimeline: SecuritySolutionStorage['addTimeline'] = (id, timeline) => { const timelines = getAllTimelines() ?? {}; storage.set(LOCAL_STORAGE_TIMELINE_KEY, { ...timelines, @@ -27,4 +27,4 @@ export const createSiemLocalStorage = (store: IStorage): SiemStorage => { return { getAllTimelines, addTimeline }; }; -export { SiemStorage }; +export { SecuritySolutionStorage }; diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts index 72a79b776acbdb..a22722350c074a 100644 --- a/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export interface SiemStorage { +export interface SecuritySolutionStorage { getAllTimelines: () => object | null; addTimeline: (id: string, timeline: unknown) => void; } diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index 2518a34a815dee..9cf0aad74071e7 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -19,7 +19,7 @@ import { alertMiddlewareFactory } from '../../../endpoint_alerts/store/middlewar import { AppRootProvider } from './app_root_provider'; import { managementMiddlewareFactory } from '../../../management/store/middleware'; import { createKibanaContextProviderMock } from '../kibana_react'; -import { SUB_PLUGINS_REDUCER, mockGlobalState, createSiemLocalStorageMock } from '..'; +import { SUB_PLUGINS_REDUCER, mockGlobalState, createSecuritySolutionStorageMock } from '..'; type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; @@ -56,13 +56,13 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { const coreStart = coreMock.createStart({ basePath: '/mock' }); const depsStart = depsStartMock(); const middlewareSpy = createSpyMiddleware(); - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); const store = createStore( mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock, + securitySolutionLocalStorageMock, [ substateMiddlewareFactory( (globalState) => globalState.alertList, diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts b/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts index 5154cc9c76ca48..76a042609144fd 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts +++ b/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts @@ -25,7 +25,7 @@ export const localStorageMock = (): IStorage => { }; }; -export const createSiemLocalStorageMock = () => ({ +export const createSecuritySolutionStorageMock = () => ({ getAllTimelines: jest.fn(), addTimeline: jest.fn(), }); diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index bf261c8dbf104c..e14c3e07114944 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -22,7 +22,7 @@ import { mockGlobalState } from './global_state'; import { createKibanaContextProviderMock } from './kibana_react'; import { FieldHook, useForm } from '../../shared_imports'; import { SUB_PLUGINS_REDUCER } from './utils'; -import { createSiemLocalStorageMock, localStorageMock } from './mock_local_storage'; +import { createSecuritySolutionStorageMock, localStorageMock } from './mock_local_storage'; const state: State = mockGlobalState; @@ -44,12 +44,17 @@ Object.defineProperty(window, 'localStorage', { }); const MockKibanaContextProvider = createKibanaContextProviderMock(); -const siemLocalStorageMock = createSiemLocalStorageMock(); +const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); /** A utility for wrapping children in the providers required to run most tests */ const TestProvidersComponent: React.FC = ({ children, - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock), + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ), onDragEnd = jest.fn(), }) => ( @@ -69,7 +74,12 @@ export const TestProviders = React.memo(TestProvidersComponent); const TestProviderWithoutDragAndDropComponent: React.FC = ({ children, - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock), + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ), }) => ( {children} diff --git a/x-pack/plugins/security_solution/public/common/store/store.ts b/x-pack/plugins/security_solution/public/common/store/store.ts index a7f5caf2c77d0b..b8316cb31b596c 100644 --- a/x-pack/plugins/security_solution/public/common/store/store.ts +++ b/x-pack/plugins/security_solution/public/common/store/store.ts @@ -28,7 +28,7 @@ import { AppApolloClient } from '../lib/lib'; import { AppAction } from './actions'; import { Immutable } from '../../../common/endpoint/types'; import { State } from './types'; -import { SiemStorage } from '../lib/local_storage'; +import { SecuritySolutionStorage } from '../lib/local_storage'; type ComposeType = typeof compose; declare global { @@ -49,7 +49,7 @@ export const createStore = ( state: PreloadedState, pluginsReducer: SubPluginsInitReducer, apolloClient: Observable, - storage: SiemStorage, + storage: SecuritySolutionStorage, additionalMiddleware?: Array>>> ): Store => { const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx b/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx index 6d4046673f8918..a5919bc454a1ab 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx @@ -19,7 +19,7 @@ import { SUB_PLUGINS_REDUCER, mockGlobalState, apolloClientObservable, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; export const alertPageTestRender = () => { @@ -30,12 +30,12 @@ export const alertPageTestRender = () => { /** * Create a store, with the middleware disabled. We don't want side effects being created by our code in this test. */ - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); const store = createStore( mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); const depsStart = depsStartMock(); diff --git a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx index 375b50061fd989..b803801606f5f6 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx @@ -13,7 +13,7 @@ import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { hostsModel } from '../../store'; @@ -25,11 +25,21 @@ describe('Authentication Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx index a5f12465a277e1..02143c720be849 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx @@ -15,7 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -36,13 +36,23 @@ jest.mock('../../../common/components/query_bar', () => ({ describe('Hosts Table', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx index d71922564b9eec..925569a6b01af4 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx @@ -19,7 +19,7 @@ import { TestProviders, mockGlobalState, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../common/mock'; import { SiemNavigation } from '../../common/components/navigation'; import { inputsActions } from '../../common/store/inputs'; @@ -172,12 +172,12 @@ describe('Hosts - rendering', () => { ]; localSource[0].result.data.source.status.indicesExist = true; const myState: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); const myStore = createStore( myState, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx index c48c33ba9cc408..89a37b50bfc485 100644 --- a/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx @@ -14,7 +14,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { networkModel } from '../../store'; @@ -27,11 +27,21 @@ import { NarrowDateRange } from '../../../common/components/ml/types'; describe('IP Overview Component', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx index df0584d664bdfe..0fd83a9ec47791 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx @@ -12,7 +12,7 @@ import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { KpiNetworkComponent } from '.'; @@ -24,11 +24,21 @@ describe('KpiNetwork Component', () => { const to = new Date('2019-06-18T06:00:00.000Z').valueOf(); const narrowDateRange = jest.fn(); - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx index 420948d14faf4d..5feaa21a05649e 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx @@ -15,7 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { State, createStore } from '../../../common/store'; import { networkModel } from '../../store'; @@ -27,12 +27,22 @@ import { mockData } from './mock'; describe('NetworkTopNFlow Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx index 0aacc9c43fef6c..c9ff8508a79421 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx @@ -15,7 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -28,12 +28,22 @@ describe('NetworkHttp Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx index 2572dba2d4f960..1eeccf3f12b8ac 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx @@ -17,7 +17,7 @@ import { mockIndexPattern, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -31,11 +31,21 @@ describe('NetworkTopCountries Table Component', () => { const state: State = mockGlobalState; const mount = useMountAppended(); - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx index f022b560be3ede..ad4568e0cc20af 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx @@ -16,7 +16,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -28,12 +28,22 @@ describe('NetworkTopNFlow Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx index f26ca5eaaa3eb0..8840651cca58ba 100644 --- a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx @@ -15,7 +15,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -27,12 +27,22 @@ describe('Tls Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('Rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx index 834407fd585573..6a9f273c8399af 100644 --- a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx @@ -16,7 +16,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -29,12 +29,22 @@ describe('Users Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); const mount = useMountAppended(); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); describe('Rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx index 64f0b7c1d53461..90c9d7cc07fd75 100644 --- a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx @@ -20,7 +20,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { createStore, State } from '../../../common/store'; @@ -119,11 +119,21 @@ describe('Ip Details', () => { }); const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); localSource = cloneDeep(mocksSource); }); diff --git a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx index 36fcb23aa4518d..e592827790d8ac 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx @@ -18,7 +18,7 @@ import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../common/mock'; import { State, createStore } from '../../common/store'; import { inputsActions } from '../../common/store/inputs'; @@ -156,12 +156,12 @@ describe('rendering - rendering', () => { ]; localSource[0].result.data.source.status.indicesExist = true; const myState: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); const myStore = createStore( myState, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx index c1c4bbc1e346dd..541c4219ad73a4 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx @@ -14,7 +14,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { OverviewHost } from '.'; @@ -93,12 +93,22 @@ const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ describe('OverviewHost', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + myState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); test('it renders the expected widget title', () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx index a265567dd87795..f3b79de0e9af65 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx @@ -13,7 +13,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { OverviewNetwork } from '.'; @@ -84,12 +84,22 @@ const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ describe('OverviewNetwork', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { const myState = cloneDeep(state); - store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + myState, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); }); test('it renders the expected widget title', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx index 2c75f53bac1207..1bcc57f9ea1a5f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx @@ -14,7 +14,7 @@ import { mockGlobalState, TestProviders, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../common/mock'; import { createStore, State } from '../../../common/store'; import { mockDataProviders } from '../timeline/data_providers/mock/mock_data_providers'; @@ -32,7 +32,7 @@ const usersViewing = ['elastic']; describe('Flyout', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); describe('rendering', () => { test('it renders correctly against snapshot', () => { @@ -62,7 +62,7 @@ describe('Flyout', () => { stateShowIsTrue, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); const wrapper = mount( @@ -86,7 +86,7 @@ describe('Flyout', () => { stateWithDataProviders, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); const wrapper = mount( @@ -108,7 +108,7 @@ describe('Flyout', () => { stateWithDataProviders, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); const wrapper = mount( @@ -142,7 +142,7 @@ describe('Flyout', () => { stateWithDataProviders, SUB_PLUGINS_REDUCER, apolloClientObservable, - siemLocalStorageMock + securitySolutionLocalStorageMock ); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx index 37be820651e964..26e03421a6818a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx @@ -13,7 +13,7 @@ import { mockGlobalState, apolloClientObservable, SUB_PLUGINS_REDUCER, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, } from '../../../../common/mock'; import { createStore, State } from '../../../../common/store'; import { useThrottledResizeObserver } from '../../../../common/components/utils'; @@ -49,12 +49,22 @@ describe('Properties', () => { const usersViewing = ['elastic']; const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); beforeEach(() => { jest.clearAllMocks(); - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); mockedWidth = 1000; }); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 45b4c903d61258..1554ebeb12c1ba 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -13,7 +13,7 @@ import { apolloClientObservable, TestProviders, defaultHeaders, - createSiemLocalStorageMock, + createSecuritySolutionStorageMock, mockIndexPattern, } from '../../../common/mock'; @@ -44,8 +44,13 @@ const wait = (ms: number = 500): Promise => { describe('epicLocalStorage', () => { const state: State = mockGlobalState; - const siemLocalStorageMock = createSiemLocalStorageMock(); - let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + let store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); let props = {} as TimelineComponentProps; const sort: Sort = { @@ -58,7 +63,12 @@ describe('epicLocalStorage', () => { const indexPattern = mockIndexPattern; beforeEach(() => { - store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, siemLocalStorageMock); + store = createStore( + state, + SUB_PLUGINS_REDUCER, + apolloClientObservable, + securitySolutionLocalStorageMock + ); props = { browserFields: mockBrowserFields, columns: defaultHeaders, @@ -103,7 +113,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(upsertColumn({ id: 'test', index: 1, column: defaultHeaders[0] })); await wait(); - expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); }); it('persist timeline when removing a column ', async () => { @@ -114,7 +124,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(removeColumn({ id: 'test', columnId: '@timestamp' })); await wait(); - expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); }); it('persists resizing of a column', async () => { @@ -125,7 +135,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(applyDeltaToColumnWidth({ id: 'test', columnId: '@timestamp', delta: 80 })); await wait(); - expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); }); it('persist the resetting of the fields', async () => { @@ -136,7 +146,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(updateColumns({ id: 'test', columns: defaultHeaders })); await wait(); - expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); }); it('persist items per page', async () => { @@ -147,7 +157,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(updateItemsPerPage({ id: 'test', itemsPerPage: 50 })); await wait(); - expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); }); it('persist the sorting of a column', async () => { @@ -166,6 +176,6 @@ describe('epicLocalStorage', () => { }) ); await wait(); - expect(siemLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts index 50057f3be08c92..5ed2a6da3928cc 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts @@ -6,7 +6,7 @@ import { Action } from 'redux'; import { Observable } from 'rxjs'; -import { SiemStorage } from '../../../common/lib/local_storage'; +import { SecuritySolutionStorage } from '../../../common/lib/local_storage'; import { AppApolloClient } from '../../../common/lib/lib'; import { inputsModel } from '../../../common/store/inputs'; import { NotesById } from '../../../common/store/app/model'; @@ -45,5 +45,5 @@ export interface TimelineEpicDependencies { selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery; selectNotesByIdSelector: (state: State) => NotesById; apolloClient$: Observable; - storage: SiemStorage; + storage: SecuritySolutionStorage; } From 0c16de3088456ac0da60cbdd95a8868174158abe Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 5 Jun 2020 13:37:34 +0300 Subject: [PATCH 28/42] Clean cypress tests --- .../cypress/screens/hosts/external_events.ts | 4 --- .../cypress/screens/hosts/main.ts | 2 -- .../cypress/screens/timeline.ts | 15 ----------- .../cypress/tasks/hosts/main.ts | 3 --- .../cypress/tasks/timeline.ts | 25 ------------------- 5 files changed, 49 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/external_events.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/external_events.ts index c337baea2eda9b..982e1ea378e703 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/external_events.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/external_events.ts @@ -5,7 +5,3 @@ */ export const TABLE_COLUMN_EVENTS_MESSAGE = 1; -export const TABLE_COLUMN_EVENTS_HOSTNAME = 2; - -export const FIELDS_MENU_CATEGORY_ID = '[data-test-subj="_id-category-count"]'; -export const FIELDS_MENU_ID = '[data-test-subj="field-_id-checkbox"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts b/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts index 585ba561d21236..3c30562680e86d 100644 --- a/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts +++ b/x-pack/plugins/security_solution/cypress/screens/hosts/main.ts @@ -12,8 +12,6 @@ export const AUTHENTICATIONS_TAB = '[data-test-subj="navigation-authentications" export const EVENTS_TAB = '[data-test-subj="navigation-events"]'; -export const EXTERNAL_ALERTS_TAB = '[data-test-subj="navigation-alerts"]'; - export const KQL_SEARCH_BAR = '[data-test-subj="queryInput"]'; export const UNCOMMON_PROCESSES_TAB = '[data-test-subj="navigation-uncommonProcesses"]'; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index e53e22d906ad0c..fde06572af811a 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -8,30 +8,17 @@ export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]'; export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]'; -export const COLUMNS = '[data-test-subj="data-driven-columns"]'; - export const DRAGGABLE_HEADER = '[data-test-subj="headers-group"] [data-test-subj="draggable-header"]'; -export const FIELDS_MENU = '[data-test-subj="show-field-browser-gear"]'; - export const HEADERS_GROUP = '[data-test-subj="headers-group"]'; -export const HEADER_SORT_BUTTON = '[data-test-subj="header-sort-button"]'; - export const ID_HEADER_FIELD = '[data-test-subj="timeline"] [data-test-subj="header-text-_id"]'; export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name-_id"]'; export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]'; -export const ITEMS_PER_PAGE = '[data-test-subj="local-events-count"]'; - -export const ITEMS_PER_PAGE_BUTTON = '[data-test-subj="local-events-count-button"]'; - -export const EVENTS_PER_PAGE_BUTTON = (itemsPerPage: number) => - `[data-test-subj="items-per-page-option-${itemsPerPage}"]`; - export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]'; export const REMOVE_COLUMN = '[data-test-subj="remove-column"]'; @@ -39,8 +26,6 @@ export const REMOVE_COLUMN = '[data-test-subj="remove-column"]'; export const RESET_FIELDS = '[data-test-subj="events-viewer-panel"] [data-test-subj="reset-fields"]'; -export const ROWS = '[data-test-subj="event"]'; - export const SEARCH_OR_FILTER_CONTAINER = '[data-test-subj="timeline-search-or-filter-search-container"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts b/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts index 7f8c960a74b25a..eba0c353fbf37c 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/hosts/main.ts @@ -9,7 +9,6 @@ import { AUTHENTICATIONS_TAB, EVENTS_TAB, UNCOMMON_PROCESSES_TAB, - EXTERNAL_ALERTS_TAB, } from '../../screens/hosts/main'; export const openAllHosts = () => cy.get(ALL_HOSTS_TAB).click({ force: true }); @@ -18,6 +17,4 @@ export const openAuthentications = () => cy.get(AUTHENTICATIONS_TAB).click({ for export const openEvents = () => cy.get(EVENTS_TAB).click({ force: true }); -export const openExternalAlerts = () => cy.get(EXTERNAL_ALERTS_TAB).click({ force: true }); - export const openUncommonProcesses = () => cy.get(UNCOMMON_PROCESSES_TAB).click({ force: true }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 97056dda83b0a7..82c072e029d116 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -21,17 +21,11 @@ import { TIMELINE_TITLE, TIMESTAMP_TOGGLE_FIELD, TOGGLE_TIMELINE_EXPAND_EVENT, - DRAGGABLE_HEADER, REMOVE_COLUMN, - FIELDS_MENU, RESET_FIELDS, - ITEMS_PER_PAGE_BUTTON, - HEADER_SORT_BUTTON, - EVENTS_PER_PAGE_BUTTON, } from '../screens/timeline'; import { drag, drop } from '../tasks/common'; -import { FIELDS_MENU_CATEGORY_ID, FIELDS_MENU_ID } from '../screens/hosts/external_events'; export const hostExistsQuery = 'host.name: *'; @@ -116,25 +110,6 @@ export const removeColumn = (column: number) => { cy.wait(3000); // wait for DOM updates }; -export const addIdColumn = () => { - cy.get(FIELDS_MENU).click({ force: true }); - cy.get(FIELDS_MENU_CATEGORY_ID).eq(0).click({ force: true }); - cy.get(FIELDS_MENU_ID).check(); - cy.get(DRAGGABLE_HEADER).eq(1).invoke('text').should('eql', '_id'); - cy.wait(3000); // wait for DOM updates -}; - export const resetFields = () => { cy.get(RESET_FIELDS).click({ force: true }); }; - -export const changeItemsPerPage = (itemsPerPage: number) => { - cy.get(ITEMS_PER_PAGE_BUTTON).click({ force: true }); - cy.get(EVENTS_PER_PAGE_BUTTON(itemsPerPage)).click(); - cy.wait(3000); // wait for DOM updates -}; - -export const sortByColumn = (column: number) => { - cy.get(HEADER_SORT_BUTTON).first().should('exist'); - cy.get(HEADER_SORT_BUTTON).eq(column).click({ force: true }); -}; From c20ae9dbe20ca26e591863d99681c2343198e85a Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 5 Jun 2020 13:37:57 +0300 Subject: [PATCH 29/42] Proper reload --- .../cypress/integration/timeline_local_storage.spec.ts | 3 ++- x-pack/plugins/security_solution/cypress/tasks/common.ts | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts index 87c2c004bbc3b6..6ad3ea564a458f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { reload } from '../tasks/common'; import { loginAndWaitForPage } from '../tasks/login'; import { HOSTS_PAGE } from '../urls/navigation'; import { openEvents } from '../tasks/hosts/main'; @@ -30,7 +31,7 @@ describe('persistent timeline', () => { .invoke('text') .should('equal', 'message'); removeColumn(TABLE_COLUMN_EVENTS_MESSAGE); - cy.reload(); + reload(waitsForEventsToBeLoaded); cy.get(DRAGGABLE_HEADER).each(($el) => { expect($el.text()).not.equal('message'); }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/common.ts b/x-pack/plugins/security_solution/cypress/tasks/common.ts index b0c64214459f0b..a385ad78f63b7b 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/common.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/common.ts @@ -48,3 +48,9 @@ export const drop = (dropTarget: JQuery) => { .trigger('mouseup', { force: true }) .wait(1000); }; + +export const reload = (afterReload: () => void) => { + cy.reload(); + cy.contains('a', 'Security'); + afterReload(); +}; From ec2df5b15b18096355f0d80474cb3946ae021749 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 9 Jun 2020 12:01:56 +0300 Subject: [PATCH 30/42] Improve cypress tests --- .../cypress/integration/timeline_local_storage.spec.ts | 7 +++++++ .../plugins/security_solution/cypress/screens/timeline.ts | 2 ++ .../plugins/security_solution/cypress/tasks/timeline.ts | 8 +++++++- .../timeline/body/column_headers/actions/index.tsx | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts index 6ad3ea564a458f..3ea99276400b4b 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts @@ -26,12 +26,19 @@ describe('persistent timeline', () => { }); it('persist the deletion of a column', () => { + const numberOfTimelineColumns = 9; + cy.get(DRAGGABLE_HEADER) .eq(TABLE_COLUMN_EVENTS_MESSAGE) .invoke('text') .should('equal', 'message'); + + cy.get(DRAGGABLE_HEADER).should('have.length', numberOfTimelineColumns); + removeColumn(TABLE_COLUMN_EVENTS_MESSAGE); reload(waitsForEventsToBeLoaded); + + cy.get(DRAGGABLE_HEADER).should('have.length', numberOfTimelineColumns - 1); cy.get(DRAGGABLE_HEADER).each(($el) => { expect($el.text()).not.equal('message'); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index fde06572af811a..c673cf34b6daeb 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -35,6 +35,8 @@ export const TIMELINE = (id: string) => { return `[data-test-subj="title-${id}"]`; }; +export const TIMELINE_COLUMN_SPINNER = '[data-test-subj="timeline-loading-spinner"]'; + export const TIMELINE_DATA_PROVIDERS = '[data-test-subj="dataProviders"]'; export const TIMELINE_DATA_PROVIDERS_EMPTY = diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 82c072e029d116..90441375d12d14 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -23,6 +23,7 @@ import { TOGGLE_TIMELINE_EXPAND_EVENT, REMOVE_COLUMN, RESET_FIELDS, + TIMELINE_COLUMN_SPINNER, } from '../screens/timeline'; import { drag, drop } from '../tasks/common'; @@ -104,10 +105,15 @@ export const dragAndDropIdToggleFieldToTimeline = () => { ); }; +export const waitForTimelineActionToFinish = () => { + cy.get(TIMELINE_COLUMN_SPINNER).should('exist'); + cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist'); +}; + export const removeColumn = (column: number) => { cy.get(REMOVE_COLUMN).first().should('exist'); cy.get(REMOVE_COLUMN).eq(column).click({ force: true }); - cy.wait(3000); // wait for DOM updates + waitForTimelineActionToFinish(); }; export const resetFields = () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx index 02ebfd7f752b55..3352639fa95f82 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx @@ -48,7 +48,7 @@ export const Actions = React.memo(({ header, onColumnRemoved, sort, isLoa <> {sort.columnId === header.id && isLoading ? ( - + ) : ( From 91a892a3129a534fce8a0cdc56029f1e2eb17d91 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 9 Jun 2020 12:47:02 +0300 Subject: [PATCH 31/42] Minor fixes --- .../public/alerts/components/alerts_table/index.test.tsx | 2 +- .../public/alerts/pages/detection_engine/constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx index 7f7c74e9b036ea..0cef13d5e80c5e 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx @@ -13,7 +13,7 @@ describe('AlertsTableComponent', () => { it('renders correctly', () => { const wrapper = shallow( Date: Tue, 9 Jun 2020 12:53:45 +0300 Subject: [PATCH 32/42] Rename ids --- .../components/alerts_viewer/alerts_table.tsx | 13 +++++++++---- .../common/components/alerts_viewer/index.tsx | 9 +++++++-- .../public/common/components/alerts_viewer/types.ts | 2 +- .../pages/navigation/alerts_query_tab_body.tsx | 2 +- .../pages/navigation/alerts_query_tab_body.tsx | 2 +- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx index 16f31e964c5a9e..a25ee3bd1456d0 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx @@ -51,19 +51,24 @@ const defaultAlertsFilters: Filter[] = [ ]; interface Props { - id: string; + timelineId: string; endDate: number; startDate: number; pageFilters?: Filter[]; } -const AlertsTableComponent: React.FC = ({ id, endDate, startDate, pageFilters = [] }) => { +const AlertsTableComponent: React.FC = ({ + timelineId, + endDate, + startDate, + pageFilters = [], +}) => { const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]); const { initializeTimeline } = useManageTimeline(); useEffect(() => { initializeTimeline({ - id, + id: timelineId, documentType: i18n.ALERTS_DOCUMENT_TYPE, footerText: i18n.TOTAL_COUNT_OF_ALERTS, title: i18n.ALERTS_TABLE_TITLE, @@ -76,7 +81,7 @@ const AlertsTableComponent: React.FC = ({ id, endDate, startDate, pageFil pageFilters={alertsFilter} defaultModel={alertsDefaultModel} end={endDate} - id={id} + id={timelineId} start={startDate} /> ); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx index a733de022d7ea8..a31cb4f2a8bfd8 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/index.tsx @@ -17,7 +17,7 @@ import { MatrixHisrogramConfigs } from '../matrix_histogram/types'; const ID = 'alertsOverTimeQuery'; export const AlertsView = ({ - tableId, + timelineId, deleteQuery, endDate, filterQuery, @@ -62,7 +62,12 @@ export const AlertsView = ({ type={type} {...alertsHistogramConfigs} /> - + ); }; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts index 6a89b28e4a6c5a..5c5245fa788b54 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/types.ts @@ -16,7 +16,7 @@ export interface AlertsComponentsProps CommonQueryProps, 'deleteQuery' | 'endDate' | 'filterQuery' | 'skip' | 'setQuery' | 'startDate' | 'type' > { - tableId: string; + timelineId: string; pageFilters: Filter[]; stackByOptions?: MatrixHistogramOption[]; defaultFilters?: Filter[]; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx index d779bb7512a049..7819e4fa62a2e2 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx @@ -50,7 +50,7 @@ export const HostAlertsQueryTabBody = React.memo((alertsProps: AlertsComponentQu [pageFilters] ); - return ; + return ; }); HostAlertsQueryTabBody.displayName = 'HostAlertsQueryTabBody'; diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx index 1c331d12ac84ea..daf2f6700070a9 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx @@ -64,7 +64,7 @@ export const filterNetworkData: Filter[] = [ ]; export const NetworkAlertsQueryTabBody = React.memo((alertsProps: NetworkComponentQueryProps) => ( - + )); NetworkAlertsQueryTabBody.displayName = 'NetworkAlertsQueryTabBody'; From 56d2c29eb2cb18507736d6fb34e8391a769da055 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 9 Jun 2020 13:44:19 +0300 Subject: [PATCH 33/42] Improve localStorage --- .../security_solution/public/app/app.tsx | 2 +- .../common/lib/local_storage/index.test.ts | 26 +++++++++---------- .../public/common/lib/local_storage/index.tsx | 8 +++--- .../public/common/lib/local_storage/types.ts | 7 +++-- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 513c80cc593cb7..27c140f696ae6d 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -90,7 +90,7 @@ const StartAppComponent: FC = ({ subPlugins, ...libs }) => { ...(subPluginsStore.initialState.timeline ?? initialTimelineState), timelineById: { ...(subPluginsStore.initialState.timeline?.timelineById ?? {}), - ...(storage.getAllTimelines() ?? {}), + ...storage.getAllTimelines(), }, }, }), diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts index 6900b766c53347..05e916522b3789 100644 --- a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.test.ts @@ -5,7 +5,7 @@ */ import { createSecuritySolutionStorage, LOCAL_STORAGE_TIMELINE_KEY } from '.'; -import { localStorageMock } from '../../mock'; +import { localStorageMock, mockTimelineModel } from '../../mock'; describe('SiemLocalStorage', () => { const localStorage = localStorageMock(); @@ -16,33 +16,33 @@ describe('SiemLocalStorage', () => { }); it('adds a timeline when storage is empty', () => { - storage.addTimeline('timeline-1', {}); + storage.addTimeline('timeline-1', mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'timeline-1': {}, + 'timeline-1': mockTimelineModel, }); }); it('adds a timeline when storage contains another timelines', () => { - storage.addTimeline('timeline-1', {}); - storage.addTimeline('timeline-2', {}); + storage.addTimeline('timeline-1', mockTimelineModel); + storage.addTimeline('timeline-2', mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'timeline-1': {}, - 'timeline-2': {}, + 'timeline-1': mockTimelineModel, + 'timeline-2': mockTimelineModel, }); }); it('gets all timelines correctly', () => { - storage.addTimeline('timeline-1', {}); - storage.addTimeline('timeline-2', {}); + storage.addTimeline('timeline-1', mockTimelineModel); + storage.addTimeline('timeline-2', mockTimelineModel); const timelines = storage.getAllTimelines(); expect(timelines).toEqual({ - 'timeline-1': {}, - 'timeline-2': {}, + 'timeline-1': mockTimelineModel, + 'timeline-2': mockTimelineModel, }); }); - it('it gets null if there is no timelines', () => { + it('returns an empty object if there is no timelines', () => { const timelines = storage.getAllTimelines(); - expect(timelines).toBe(null); + expect(timelines).toEqual({}); }); }); diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx index 2941281de2fa22..d619433cbe817b 100644 --- a/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/index.tsx @@ -8,16 +8,16 @@ import { Storage, IStorage } from '../../../../../../../src/plugins/kibana_utils import { SecuritySolutionStorage } from './types'; export const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; +const EMPTY_TIMELINE = {}; export const createSecuritySolutionStorage = (store: IStorage): SecuritySolutionStorage => { const storage = new Storage(store); - const getAllTimelines: SecuritySolutionStorage['getAllTimelines'] = () => { - return storage.get(LOCAL_STORAGE_TIMELINE_KEY); - }; + const getAllTimelines: SecuritySolutionStorage['getAllTimelines'] = () => + storage.get(LOCAL_STORAGE_TIMELINE_KEY) ?? EMPTY_TIMELINE; const addTimeline: SecuritySolutionStorage['addTimeline'] = (id, timeline) => { - const timelines = getAllTimelines() ?? {}; + const timelines = getAllTimelines(); storage.set(LOCAL_STORAGE_TIMELINE_KEY, { ...timelines, [id]: timeline, diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts b/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts index a22722350c074a..c03b4da7706694 100644 --- a/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts +++ b/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { TimelineModel } from '../../../timelines/store/timeline/model'; + +type TimelineId = string; export interface SecuritySolutionStorage { - getAllTimelines: () => object | null; - addTimeline: (id: string, timeline: unknown) => void; + getAllTimelines: () => Record; + addTimeline: (id: string, timeline: TimelineModel) => void; } From 605466c2d0fd879f8cb2c8bd8586a5823c7851a6 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Tue, 9 Jun 2020 11:04:33 -0400 Subject: [PATCH 34/42] re-structure code --- .../security_solution/public/app/app.tsx | 40 +++++++------------ .../security_solution/public/app/types.ts | 2 + .../security_solution/public/hosts/index.ts | 22 +++++++++- .../security_solution/public/plugin.tsx | 5 ++- .../containers}/local_storage/index.test.ts | 0 .../containers}/local_storage/index.tsx | 23 ++++++++--- .../containers}/local_storage/types.ts | 5 ++- .../plugins/security_solution/public/types.ts | 2 + 8 files changed, 64 insertions(+), 35 deletions(-) rename x-pack/plugins/security_solution/public/{common/lib => timelines/containers}/local_storage/index.test.ts (100%) rename x-pack/plugins/security_solution/public/{common/lib => timelines/containers}/local_storage/index.tsx (50%) rename x-pack/plugins/security_solution/public/{common/lib => timelines/containers}/local_storage/types.ts (66%) diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 27c140f696ae6d..b5696c889d16e8 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -33,8 +33,6 @@ import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; import { ApolloClientContext } from '../common/utils/apollo_context'; import { SecuritySubPlugins } from './types'; -import { createSecuritySolutionStorage } from '../common/lib/local_storage'; -import { initialTimelineState } from '../timelines/store/timeline/reducer'; interface AppPluginRootComponentProps { apolloClient: AppApolloClient; @@ -74,26 +72,17 @@ const AppPluginRoot = memo(AppPluginRootComponent); interface StartAppComponent extends AppFrontendLibs { subPlugins: SecuritySubPlugins; + storage: Storage; } -const StartAppComponent: FC = ({ subPlugins, ...libs }) => { +const StartAppComponent: FC = ({ subPlugins, storage, ...libs }) => { const { routes: subPluginRoutes, store: subPluginsStore } = subPlugins; const { i18n } = useKibana().services; const history = createHashHistory(); const libs$ = new BehaviorSubject(libs); - const storage = createSecuritySolutionStorage(localStorage); const store = createStore( - createInitialState({ - ...subPluginsStore.initialState, - timeline: { - ...(subPluginsStore.initialState.timeline ?? initialTimelineState), - timelineById: { - ...(subPluginsStore.initialState.timeline?.timelineById ?? {}), - ...storage.getAllTimelines(), - }, - }, - }), + createInitialState(subPluginsStore.initialState), subPluginsStore.reducer, libs$.pipe(pluck('apolloClient')), storage, @@ -131,16 +120,17 @@ interface SiemAppComponentProps { subPlugins: SecuritySubPlugins; } -const SiemAppComponent: React.FC = ({ services, subPlugins }) => ( - - - -); +const SiemAppComponent: React.FC = ({ services, subPlugins }) => { + return ( + + + + ); +}; export const SiemApp = memo(SiemAppComponent); diff --git a/x-pack/plugins/security_solution/public/app/types.ts b/x-pack/plugins/security_solution/public/app/types.ts index 4b00dee3b75108..7a905b35710c99 100644 --- a/x-pack/plugins/security_solution/public/app/types.ts +++ b/x-pack/plugins/security_solution/public/app/types.ts @@ -18,6 +18,7 @@ import { NavTab } from '../common/components/navigation/types'; import { State, SubPluginsInitReducer } from '../common/store'; import { Immutable } from '../../common/endpoint/types'; import { AppAction } from '../common/store/actions'; +import { TimelineState } from '../timelines/store/timeline/types'; export enum SiemPageName { overview = 'overview', @@ -48,6 +49,7 @@ export interface SecuritySubPluginStore export interface SecuritySubPlugin { routes: React.ReactElement[]; + storageTimelines?: Pick; } type SecuritySubPluginKeyStore = diff --git a/x-pack/plugins/security_solution/public/hosts/index.ts b/x-pack/plugins/security_solution/public/hosts/index.ts index 6f27428e71c27c..7c073804feb299 100644 --- a/x-pack/plugins/security_solution/public/hosts/index.ts +++ b/x-pack/plugins/security_solution/public/hosts/index.ts @@ -4,16 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { SecuritySubPluginWithStore } from '../app/types'; import { getHostsRoutes } from './routes'; import { initialHostsState, hostsReducer, HostsState } from './store'; +import { TimelineId } from '../timelines/containers/local_storage/types'; +import { getTimelineInStorageById } from '../timelines/containers/local_storage'; + +const HOST_TIMELINE_IDS: TimelineId[] = ['hosts-page-events', 'hosts-page-external-alerts']; export class Hosts { public setup() {} - public start(): SecuritySubPluginWithStore<'hosts', HostsState> { + public start(storage: Storage): SecuritySubPluginWithStore<'hosts', HostsState> { + const hostTimelines = HOST_TIMELINE_IDS.reduce( + (acc, timelineId) => { + const timelineModel = getTimelineInStorageById(storage, timelineId); + return { + ...acc, + timelineById: { + ...acc.timelineById, + [timelineId]: timelineModel, + }, + }; + }, + { timelineById: {} } + ); + return { routes: getHostsRoutes(), + storageTimelines: hostTimelines, store: { initialState: { hosts: initialHostsState }, reducer: { hosts: hostsReducer }, diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index e3d3062ee9cf7f..b52df99892ef5b 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -14,6 +14,7 @@ import { Plugin as IPlugin, DEFAULT_APP_CATEGORIES, } from '../../../../src/core/public'; +import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { initTelemetry } from './common/lib/telemetry'; import { KibanaServices } from './common/lib/kibana/services'; @@ -50,12 +51,14 @@ export class Plugin implements IPlugin { + const storage = new Storage(localStorage); const [coreStart, startPlugins] = await core.getStartServices(); const { renderApp } = await import('./app'); const services = { ...coreStart, ...startPlugins, security: plugins.security, + storage, } as StartServices; const alertsSubPlugin = new (await import('./alerts')).Alerts(); @@ -69,7 +72,7 @@ export class Plugin implements IPlugin { - const storage = new Storage(store); +export const getTimelineInStorageById = (storage: Storage, id: TimelineId) => { + const timelines = storage.get(LOCAL_STORAGE_TIMELINE_KEY); + if (timelines != null) { + return timelines[id]; + } + return null; +}; + +export const useTimelinesStorage = (store: IStorage): SecuritySolutionStorage => { + const { storage } = useKibana().services; const getAllTimelines: SecuritySolutionStorage['getAllTimelines'] = () => - storage.get(LOCAL_STORAGE_TIMELINE_KEY) ?? EMPTY_TIMELINE; + storage.get(LOCAL_STORAGE_TIMELINE_KEY) ?? null; + + const getTimelineById: SecuritySolutionStorage['getTimelineById'] = (id: TimelineId) => + getTimelineInStorageById(storage, id); const addTimeline: SecuritySolutionStorage['addTimeline'] = (id, timeline) => { const timelines = getAllTimelines(); @@ -24,7 +35,7 @@ export const createSecuritySolutionStorage = (store: IStorage): SecuritySolution }); }; - return { getAllTimelines, addTimeline }; + return { getAllTimelines, getTimelineById, addTimeline }; }; export { SecuritySolutionStorage }; diff --git a/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts similarity index 66% rename from x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts rename to x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts index c03b4da7706694..ac9f0af0e08517 100644 --- a/x-pack/plugins/security_solution/public/common/lib/local_storage/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts @@ -6,8 +6,9 @@ import { TimelineModel } from '../../../timelines/store/timeline/model'; -type TimelineId = string; +export type TimelineId = 'hosts-page-events' | 'hosts-page-external-alerts' | string; export interface SecuritySolutionStorage { - getAllTimelines: () => Record; + getAllTimelines: () => Record; + getTimelineById: (id: TimelineId) => TimelineModel | null; addTimeline: (id: string, timeline: TimelineModel) => void; } diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 6c338d47cf63ce..6d59824702cfa9 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -12,6 +12,7 @@ import { Start as NewsfeedStart } from '../../../../src/plugins/newsfeed/public' import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; +import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { IngestManagerStart } from '../../ingest_manager/public'; import { TriggersAndActionsUIPublicPluginSetup as TriggersActionsSetup, @@ -39,6 +40,7 @@ export interface StartPlugins { export type StartServices = CoreStart & StartPlugins & { security: SecurityPluginSetup; + storage: Storage; }; // eslint-disable-next-line @typescript-eslint/no-empty-interface From b9fd416eeb8972bf12ebf2f17628906637eb9665 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 9 Jun 2020 18:54:08 +0300 Subject: [PATCH 35/42] Draft --- .../public/timelines/containers/local_storage/index.tsx | 2 +- .../public/timelines/containers/local_storage/types.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx index 885c1732bac816..668362f2e34fd4 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx @@ -22,7 +22,7 @@ export const useTimelinesStorage = (store: IStorage): SecuritySolutionStorage => const { storage } = useKibana().services; const getAllTimelines: SecuritySolutionStorage['getAllTimelines'] = () => - storage.get(LOCAL_STORAGE_TIMELINE_KEY) ?? null; + storage.get(LOCAL_STORAGE_TIMELINE_KEY); const getTimelineById: SecuritySolutionStorage['getTimelineById'] = (id: TimelineId) => getTimelineInStorageById(storage, id); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts index ac9f0af0e08517..259ad0dcb290cc 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts @@ -6,9 +6,9 @@ import { TimelineModel } from '../../../timelines/store/timeline/model'; -export type TimelineId = 'hosts-page-events' | 'hosts-page-external-alerts' | string; +export type TimelineId = 'hosts-page-events' | 'hosts-page-external-alerts'; export interface SecuritySolutionStorage { - getAllTimelines: () => Record; + getAllTimelines: () => Record | null; getTimelineById: (id: TimelineId) => TimelineModel | null; addTimeline: (id: string, timeline: TimelineModel) => void; } From 60d6319481ac4c798ccde745347bf816dadaf7a3 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 10 Jun 2020 17:29:57 +0300 Subject: [PATCH 36/42] Fix types and tests to align to the new restructure of code --- .../index.test.tsx | 16 +---- .../error_toast_dispatcher/index.test.tsx | 16 +---- .../common/components/inspect/index.test.tsx | 23 ++------ .../components/stat_items/index.test.tsx | 9 +-- .../super_date_picker/index.test.tsx | 16 +---- .../common/components/top_n/index.test.tsx | 9 +-- .../mock/endpoint/app_context_render.tsx | 24 +++----- .../public/common/mock/mock_local_storage.ts | 13 ++-- .../public/common/mock/test_providers.tsx | 16 +---- .../public/common/store/store.ts | 4 +- .../view/test_helpers/render_alert_page.tsx | 9 +-- .../authentications_table/index.test.tsx | 16 +---- .../components/hosts_table/index.test.tsx | 16 +---- .../public/hosts/pages/hosts.test.tsx | 9 +-- .../components/ip_overview/index.test.tsx | 16 +---- .../components/kpi_network/index.test.tsx | 16 +---- .../network_dns_table/index.test.tsx | 16 +---- .../network_http_table/index.test.tsx | 16 +---- .../index.test.tsx | 16 +---- .../network_top_n_flow_table/index.test.tsx | 16 +---- .../components/tls_table/index.test.tsx | 16 +---- .../components/users_table/index.test.tsx | 16 +---- .../network/pages/ip_details/index.test.tsx | 16 +---- .../public/network/pages/network.test.tsx | 9 +-- .../components/overview_host/index.test.tsx | 16 +---- .../overview_network/index.test.tsx | 16 +---- .../components/flyout/index.test.tsx | 10 ++-- .../timeline/properties/index.test.tsx | 16 +---- .../properties/new_template_timeline.test.tsx | 9 +-- .../containers/local_storage/index.test.ts | 59 +++++++++++++------ .../containers/local_storage/index.tsx | 35 ++++++----- .../containers/local_storage/types.ts | 4 +- .../timeline/epic_local_storage.test.tsx | 33 +++++------ .../store/timeline/epic_local_storage.ts | 10 ++-- .../public/timelines/store/timeline/types.ts | 4 +- 35 files changed, 180 insertions(+), 381 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx index 3bc824dc5c4c7e..c7015ed81701ef 100644 --- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.test.tsx @@ -34,21 +34,11 @@ jest.mock('../../lib/kibana', () => ({ describe('AddFilterToGlobalSearchBar Component', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); mockAddFilters.mockClear(); }); diff --git a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx index 18d4b6da247108..4bc77555f09bdd 100644 --- a/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/error_toast_dispatcher/index.test.tsx @@ -21,21 +21,11 @@ import { State } from '../../store/types'; describe('Error Toast Dispatcher', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx index 290d2af6255349..45397921a6651d 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx @@ -26,7 +26,7 @@ describe('Inspect Button', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); const refetch = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + const { storage } = createSecuritySolutionStorageMock(); const newQuery: UpdateQueryParams = { inputId: 'global', id: 'myQuery', @@ -36,23 +36,13 @@ describe('Inspect Button', () => { state: state.inputs, }; - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); describe('Render', () => { beforeEach(() => { const myState = cloneDeep(state); myState.inputs = upsertQuery(newQuery); - store = createStore( - myState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); test('Eui Empty Button', () => { const wrapper = mount( @@ -156,12 +146,7 @@ describe('Inspect Button', () => { response: ['my response'], }; myState.inputs = upsertQuery(myQuery); - store = createStore( - myState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); test('Open Inspect Modal', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx index 8cb1ba6743a0b8..50721ef3b26ad7 100644 --- a/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/stat_items/index.test.tsx @@ -54,13 +54,8 @@ jest.mock('../charts/barchart', () => { describe('Stat Items Component', () => { const theme = () => ({ eui: euiDarkVars, darkMode: true }); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - const store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); describe.each([ [ diff --git a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx index bca3305c352ece..99510a1b4b42ef 100644 --- a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.test.tsx @@ -80,22 +80,12 @@ const timepickerRanges = [ describe('SIEM Super Date Picker', () => { describe('#SuperDatePicker', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { jest.clearAllMocks(); - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); mockUseUiSetting$.mockImplementation((key, defaultValue) => { const useUiSetting$Mock = createUseUiSetting$Mock(); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index dc1958464382f4..1c24c325dc9ec5 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -143,13 +143,8 @@ const state: State = { }, }; -const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); -const store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock -); +const { storage } = createSecuritySolutionStorageMock(); +const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); describe('StatefulTopN', () => { // Suppress warnings about "react-beautiful-dnd" diff --git a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx index 9cf0aad74071e7..1db63897a88633 100644 --- a/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/endpoint/app_context_render.tsx @@ -56,22 +56,16 @@ export const createAppRootMockRenderer = (): AppContextTestRender => { const coreStart = coreMock.createStart({ basePath: '/mock' }); const depsStart = depsStartMock(); const middlewareSpy = createSpyMiddleware(); - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + const { storage } = createSecuritySolutionStorageMock(); - const store = createStore( - mockGlobalState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock, - [ - substateMiddlewareFactory( - (globalState) => globalState.alertList, - alertMiddlewareFactory(coreStart, depsStart) - ), - ...managementMiddlewareFactory(coreStart, depsStart), - middlewareSpy.actionSpyMiddleware, - ] - ); + const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage, [ + substateMiddlewareFactory( + (globalState) => globalState.alertList, + alertMiddlewareFactory(coreStart, depsStart) + ), + ...managementMiddlewareFactory(coreStart, depsStart), + middlewareSpy.actionSpyMiddleware, + ]); const MockKibanaContextProvider = createKibanaContextProviderMock(); diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts b/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts index 76a042609144fd..ca44295b758891 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts +++ b/x-pack/plugins/security_solution/public/common/mock/mock_local_storage.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IStorage } from '../../../../../../src/plugins/kibana_utils/public'; +import { IStorage, Storage } from '../../../../../../src/plugins/kibana_utils/public'; export const localStorageMock = (): IStorage => { let store: Record = {}; @@ -25,7 +25,10 @@ export const localStorageMock = (): IStorage => { }; }; -export const createSecuritySolutionStorageMock = () => ({ - getAllTimelines: jest.fn(), - addTimeline: jest.fn(), -}); +export const createSecuritySolutionStorageMock = () => { + const localStorage = localStorageMock(); + return { + localStorage, + storage: new Storage(localStorage), + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx index e14c3e07114944..0573f049c35c53 100644 --- a/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/security_solution/public/common/mock/test_providers.tsx @@ -44,17 +44,12 @@ Object.defineProperty(window, 'localStorage', { }); const MockKibanaContextProvider = createKibanaContextProviderMock(); -const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); +const { storage } = createSecuritySolutionStorageMock(); /** A utility for wrapping children in the providers required to run most tests */ const TestProvidersComponent: React.FC = ({ children, - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ), + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage), onDragEnd = jest.fn(), }) => ( @@ -74,12 +69,7 @@ export const TestProviders = React.memo(TestProvidersComponent); const TestProviderWithoutDragAndDropComponent: React.FC = ({ children, - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ), + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage), }) => ( {children} diff --git a/x-pack/plugins/security_solution/public/common/store/store.ts b/x-pack/plugins/security_solution/public/common/store/store.ts index b8316cb31b596c..5f53724b287df8 100644 --- a/x-pack/plugins/security_solution/public/common/store/store.ts +++ b/x-pack/plugins/security_solution/public/common/store/store.ts @@ -28,7 +28,7 @@ import { AppApolloClient } from '../lib/lib'; import { AppAction } from './actions'; import { Immutable } from '../../../common/endpoint/types'; import { State } from './types'; -import { SecuritySolutionStorage } from '../lib/local_storage'; +import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; type ComposeType = typeof compose; declare global { @@ -49,7 +49,7 @@ export const createStore = ( state: PreloadedState, pluginsReducer: SubPluginsInitReducer, apolloClient: Observable, - storage: SecuritySolutionStorage, + storage: Storage, additionalMiddleware?: Array>>> ): Store => { const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx b/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx index a5919bc454a1ab..935186010f5860 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/view/test_helpers/render_alert_page.tsx @@ -30,13 +30,8 @@ export const alertPageTestRender = () => { /** * Create a store, with the middleware disabled. We don't want side effects being created by our code in this test. */ - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - const store = createStore( - mockGlobalState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const depsStart = depsStartMock(); depsStart.data.ui.SearchBar.mockImplementation(() =>
); diff --git a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx index b803801606f5f6..3809d848759cc3 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/authentications_table/index.test.tsx @@ -25,21 +25,11 @@ describe('Authentication Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx index 02143c720be849..1168f4f7454c81 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx @@ -36,23 +36,13 @@ jest.mock('../../../common/components/query_bar', () => ({ describe('Hosts Table', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + const { storage } = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const mount = useMountAppended(); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx index 925569a6b01af4..85db3b4e159f12 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.test.tsx @@ -172,13 +172,8 @@ describe('Hosts - rendering', () => { ]; localSource[0].result.data.source.status.indicesExist = true; const myState: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - const myStore = createStore( - myState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + const myStore = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx index 89a37b50bfc485..553cb8c63db987 100644 --- a/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/ip_overview/index.test.tsx @@ -27,21 +27,11 @@ import { NarrowDateRange } from '../../../common/components/ml/types'; describe('IP Overview Component', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx index 0fd83a9ec47791..580a5420f1c345 100644 --- a/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/kpi_network/index.test.tsx @@ -24,21 +24,11 @@ describe('KpiNetwork Component', () => { const to = new Date('2019-06-18T06:00:00.000Z').valueOf(); const narrowDateRange = jest.fn(); - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx index 5feaa21a05649e..036ebedd6b88ef 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_dns_table/index.test.tsx @@ -27,22 +27,12 @@ import { mockData } from './mock'; describe('NetworkTopNFlow Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const mount = useMountAppended(); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx index c9ff8508a79421..39fad58ca3528b 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx @@ -28,22 +28,12 @@ describe('NetworkHttp Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const mount = useMountAppended(); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx index 1eeccf3f12b8ac..8b1dbc8c558b6e 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_top_countries_table/index.test.tsx @@ -31,21 +31,11 @@ describe('NetworkTopCountries Table Component', () => { const state: State = mockGlobalState; const mount = useMountAppended(); - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx index ad4568e0cc20af..4de04f673d879f 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx @@ -28,22 +28,12 @@ describe('NetworkTopNFlow Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const mount = useMountAppended(); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx index 8840651cca58ba..acbe974f914d7e 100644 --- a/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/tls_table/index.test.tsx @@ -27,22 +27,12 @@ describe('Tls Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const mount = useMountAppended(); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('Rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx index 6a9f273c8399af..f0d4d7fbeefc60 100644 --- a/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/users_table/index.test.tsx @@ -29,22 +29,12 @@ describe('Users Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const mount = useMountAppended(); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); describe('Rendering', () => { diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx index 90c9d7cc07fd75..bbb964ae17b9f0 100644 --- a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.test.tsx @@ -119,21 +119,11 @@ describe('Ip Details', () => { }); const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); localSource = cloneDeep(mocksSource); }); diff --git a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx index e592827790d8ac..e1078dee3eb0d7 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.test.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.test.tsx @@ -156,13 +156,8 @@ describe('rendering - rendering', () => { ]; localSource[0].result.data.source.status.indicesExist = true; const myState: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - const myStore = createStore( - myState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + const myStore = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx index 541c4219ad73a4..8b04c329731ab8 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx @@ -93,22 +93,12 @@ const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ describe('OverviewHost', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { const myState = cloneDeep(state); - store = createStore( - myState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); test('it renders the expected widget title', () => { diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx index f3b79de0e9af65..2fcf6f83f0ae0e 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx @@ -84,22 +84,12 @@ const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ describe('OverviewNetwork', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { const myState = cloneDeep(state); - store = createStore( - myState, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(myState, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); }); test('it renders the expected widget title', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx index 1bcc57f9ea1a5f..932cde32f3d43c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx @@ -32,7 +32,7 @@ const usersViewing = ['elastic']; describe('Flyout', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + const { storage } = createSecuritySolutionStorageMock(); describe('rendering', () => { test('it renders correctly against snapshot', () => { @@ -62,7 +62,7 @@ describe('Flyout', () => { stateShowIsTrue, SUB_PLUGINS_REDUCER, apolloClientObservable, - securitySolutionLocalStorageMock + storage ); const wrapper = mount( @@ -86,7 +86,7 @@ describe('Flyout', () => { stateWithDataProviders, SUB_PLUGINS_REDUCER, apolloClientObservable, - securitySolutionLocalStorageMock + storage ); const wrapper = mount( @@ -108,7 +108,7 @@ describe('Flyout', () => { stateWithDataProviders, SUB_PLUGINS_REDUCER, apolloClientObservable, - securitySolutionLocalStorageMock + storage ); const wrapper = mount( @@ -142,7 +142,7 @@ describe('Flyout', () => { stateWithDataProviders, SUB_PLUGINS_REDUCER, apolloClientObservable, - securitySolutionLocalStorageMock + storage ); const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx index e4d9bd3640060b..87b0b3f30d2d8c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx @@ -65,24 +65,14 @@ jest.mock('./use_create_timeline', () => ({ describe('Properties', () => { const usersViewing = ['elastic']; const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); + const { storage } = createSecuritySolutionStorageMock(); let mockedWidth = 1000; - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); beforeEach(() => { jest.clearAllMocks(); - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); (useThrottledResizeObserver as jest.Mock).mockReturnValue({ width: mockedWidth }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx index df75c8e47a9c25..cef931ae5c345b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx @@ -25,13 +25,8 @@ jest.mock('../../../../common/lib/kibana', () => { describe('NewTemplateTimeline', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - const store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); const mockClosePopover = jest.fn(); const mockTitle = 'NEW_TIMELINE'; let wrapper: ReactWrapper; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts index 05e916522b3789..89b54181ebb4df 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts @@ -4,45 +4,68 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createSecuritySolutionStorage, LOCAL_STORAGE_TIMELINE_KEY } from '.'; -import { localStorageMock, mockTimelineModel } from '../../mock'; +import { LOCAL_STORAGE_TIMELINE_KEY, useTimelinesStorage } from '.'; +import { mockTimelineModel, createSecuritySolutionStorageMock } from '../../../common/mock'; +import { useKibana } from '../../../common/lib/kibana'; +import { createUseKibanaMock } from '../../../common/mock/kibana_react'; + +jest.mock('../../../common/lib/kibana'); + +const useKibanaMock = useKibana as jest.Mock; describe('SiemLocalStorage', () => { - const localStorage = localStorageMock(); - const storage = createSecuritySolutionStorage(localStorage); + const { localStorage, storage } = createSecuritySolutionStorageMock(); beforeEach(() => { + jest.resetAllMocks(); + useKibanaMock.mockImplementation(() => ({ + services: { + ...createUseKibanaMock()().services, + storage, + }, + })); localStorage.clear(); }); it('adds a timeline when storage is empty', () => { - storage.addTimeline('timeline-1', mockTimelineModel); + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'timeline-1': mockTimelineModel, + 'hosts-page-events': mockTimelineModel, }); }); it('adds a timeline when storage contains another timelines', () => { - storage.addTimeline('timeline-1', mockTimelineModel); - storage.addTimeline('timeline-2', mockTimelineModel); + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'timeline-1': mockTimelineModel, - 'timeline-2': mockTimelineModel, + 'hosts-page-events': mockTimelineModel, + 'hosts-page-external-alerts': mockTimelineModel, }); }); it('gets all timelines correctly', () => { - storage.addTimeline('timeline-1', mockTimelineModel); - storage.addTimeline('timeline-2', mockTimelineModel); - const timelines = storage.getAllTimelines(); + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + const timelines = timelineStorage.getAllTimelines(); expect(timelines).toEqual({ - 'timeline-1': mockTimelineModel, - 'timeline-2': mockTimelineModel, + 'hosts-page-events': mockTimelineModel, + 'hosts-page-external-alerts': mockTimelineModel, }); }); - it('returns an empty object if there is no timelines', () => { - const timelines = storage.getAllTimelines(); - expect(timelines).toEqual({}); + it('returns null if there is no timelines', () => { + const timelineStorage = useTimelinesStorage(); + const timelines = timelineStorage.getAllTimelines(); + expect(timelines).toEqual(null); + }); + + it('gets a timeline by id', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + const timeline = timelineStorage.getTimelineById('hosts-page-events'); + expect(timeline).toEqual(mockTimelineModel); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx index 668362f2e34fd4..ba93d32fcb1a72 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Storage, IStorage } from '../../../../../../../src/plugins/kibana_utils/public'; -import { SecuritySolutionStorage, TimelineId } from './types'; +import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; +import { TimelinesStorage, TimelineId } from './types'; import { useKibana } from '../../../common/lib/kibana'; +import { TimelineModel } from '../../store/timeline/model'; export const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; @@ -18,24 +19,30 @@ export const getTimelineInStorageById = (storage: Storage, id: TimelineId) => { return null; }; -export const useTimelinesStorage = (store: IStorage): SecuritySolutionStorage => { +export const getAllTimelinesInStorage = (storage: Storage) => + storage.get(LOCAL_STORAGE_TIMELINE_KEY); + +export const addTimelineInStorage = (storage: Storage, id: TimelineId, timeline: TimelineModel) => { + const timelines = getAllTimelinesInStorage(storage); + storage.set(LOCAL_STORAGE_TIMELINE_KEY, { + ...timelines, + [id]: timeline, + }); +}; + +export const useTimelinesStorage = (): TimelinesStorage => { const { storage } = useKibana().services; - const getAllTimelines: SecuritySolutionStorage['getAllTimelines'] = () => - storage.get(LOCAL_STORAGE_TIMELINE_KEY); + const getAllTimelines: TimelinesStorage['getAllTimelines'] = () => + getAllTimelinesInStorage(storage); - const getTimelineById: SecuritySolutionStorage['getTimelineById'] = (id: TimelineId) => + const getTimelineById: TimelinesStorage['getTimelineById'] = (id: TimelineId) => getTimelineInStorageById(storage, id); - const addTimeline: SecuritySolutionStorage['addTimeline'] = (id, timeline) => { - const timelines = getAllTimelines(); - storage.set(LOCAL_STORAGE_TIMELINE_KEY, { - ...timelines, - [id]: timeline, - }); - }; + const addTimeline: TimelinesStorage['addTimeline'] = (id, timeline) => + addTimelineInStorage(storage, id, timeline); return { getAllTimelines, getTimelineById, addTimeline }; }; -export { SecuritySolutionStorage }; +export { TimelinesStorage }; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts index 259ad0dcb290cc..eda64df57f252d 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts @@ -7,8 +7,8 @@ import { TimelineModel } from '../../../timelines/store/timeline/model'; export type TimelineId = 'hosts-page-events' | 'hosts-page-external-alerts'; -export interface SecuritySolutionStorage { +export interface TimelinesStorage { getAllTimelines: () => Record | null; getTimelineById: (id: TimelineId) => TimelineModel | null; - addTimeline: (id: string, timeline: TimelineModel) => void; + addTimeline: (id: TimelineId, timeline: TimelineModel) => void; } diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 1554ebeb12c1ba..34778aba7873cc 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -36,21 +36,21 @@ import { mockDataProviders } from '../../components/timeline/data_providers/mock import { Sort } from '../../components/timeline/body/sort'; import { Direction } from '../../../graphql/types'; +import { addTimelineInStorage } from '../../containers/local_storage'; import { isPageTimeline } from './epic_local_storage'; +jest.mock('../../containers/local_storage'); + const wait = (ms: number = 500): Promise => { return new Promise((resolve) => setTimeout(resolve, ms)); }; +const addTimelineInStorageMock = addTimelineInStorage as jest.Mock; + describe('epicLocalStorage', () => { const state: State = mockGlobalState; - const securitySolutionLocalStorageMock = createSecuritySolutionStorageMock(); - let store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + const { storage } = createSecuritySolutionStorageMock(); + let store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); let props = {} as TimelineComponentProps; const sort: Sort = { @@ -63,12 +63,7 @@ describe('epicLocalStorage', () => { const indexPattern = mockIndexPattern; beforeEach(() => { - store = createStore( - state, - SUB_PLUGINS_REDUCER, - apolloClientObservable, - securitySolutionLocalStorageMock - ); + store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, storage); props = { browserFields: mockBrowserFields, columns: defaultHeaders, @@ -113,7 +108,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(upsertColumn({ id: 'test', index: 1, column: defaultHeaders[0] })); await wait(); - expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(addTimelineInStorageMock).toHaveBeenCalled(); }); it('persist timeline when removing a column ', async () => { @@ -124,7 +119,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(removeColumn({ id: 'test', columnId: '@timestamp' })); await wait(); - expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(addTimelineInStorageMock).toHaveBeenCalled(); }); it('persists resizing of a column', async () => { @@ -135,7 +130,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(applyDeltaToColumnWidth({ id: 'test', columnId: '@timestamp', delta: 80 })); await wait(); - expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(addTimelineInStorageMock).toHaveBeenCalled(); }); it('persist the resetting of the fields', async () => { @@ -146,7 +141,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(updateColumns({ id: 'test', columns: defaultHeaders })); await wait(); - expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(addTimelineInStorageMock).toHaveBeenCalled(); }); it('persist items per page', async () => { @@ -157,7 +152,7 @@ describe('epicLocalStorage', () => { ); store.dispatch(updateItemsPerPage({ id: 'test', itemsPerPage: 50 })); await wait(); - expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(addTimelineInStorageMock).toHaveBeenCalled(); }); it('persist the sorting of a column', async () => { @@ -176,6 +171,6 @@ describe('epicLocalStorage', () => { }) ); await wait(); - expect(securitySolutionLocalStorageMock.addTimeline).toHaveBeenCalled(); + expect(addTimelineInStorageMock).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts index 4bfc6ea3909656..511dc8311430ae 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts @@ -9,6 +9,9 @@ import { map, filter, ignoreElements, tap, withLatestFrom, delay } from 'rxjs/op import { Epic } from 'redux-observable'; import { get } from 'lodash/fp'; +import { addTimelineInStorage } from '../../containers/local_storage'; +import { TimelineId } from '../../containers/local_storage/types'; + import { removeColumn, upsertColumn, @@ -40,16 +43,15 @@ export const createTimelineLocalStorageEpic = (): Epic< TimelineEpicDependencies > => (action$, state$, { timelineByIdSelector, storage }) => { const timeline$ = state$.pipe(map(timelineByIdSelector), filter(isNotNull)); - return action$.pipe( delay(500), withLatestFrom(timeline$), filter(([action]) => isPageTimeline(get('payload.id', action))), tap(([action, timelineById]) => { if (timelineActionTypes.includes(action.type)) { - if (storage && storage.addTimeline) { - const timelineId: string = get('payload.id', action); - storage.addTimeline(timelineId, timelineById[timelineId]); + if (storage) { + const timelineId: TimelineId = get('payload.id', action); + addTimelineInStorage(storage, timelineId, timelineById[timelineId]); } } }), diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts index 5ed2a6da3928cc..a3383950961f23 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts @@ -6,7 +6,7 @@ import { Action } from 'redux'; import { Observable } from 'rxjs'; -import { SecuritySolutionStorage } from '../../../common/lib/local_storage'; +import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; import { AppApolloClient } from '../../../common/lib/lib'; import { inputsModel } from '../../../common/store/inputs'; import { NotesById } from '../../../common/store/app/model'; @@ -45,5 +45,5 @@ export interface TimelineEpicDependencies { selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery; selectNotesByIdSelector: (state: State) => NotesById; apolloClient$: Observable; - storage: SecuritySolutionStorage; + storage: Storage; } From 40b02fb81c6809578d04a4340b29b2c89ee70f8f Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 10 Jun 2020 20:44:29 +0300 Subject: [PATCH 37/42] Improve code --- .../{pages/detection_engine => }/constants.ts | 4 +-- .../security_solution/public/alerts/index.ts | 28 ++++++++++++++++++- .../detection_engine/detection_engine.tsx | 2 +- .../detection_engine/rules/details/index.tsx | 2 +- .../public/hosts/constants.ts | 8 ++++++ .../security_solution/public/hosts/index.ts | 13 +++++++-- .../public/network/constants.ts | 7 +++++ .../security_solution/public/network/index.ts | 11 +++++++- .../navigation/alerts_query_tab_body.tsx | 9 ++++-- .../security_solution/public/plugin.tsx | 18 ++++++++++-- .../public/timelines/containers/index.tsx | 2 +- .../containers/local_storage/types.ts | 13 ++++++++- 12 files changed, 100 insertions(+), 17 deletions(-) rename x-pack/plugins/security_solution/public/alerts/{pages/detection_engine => }/constants.ts (65%) create mode 100644 x-pack/plugins/security_solution/public/hosts/constants.ts create mode 100644 x-pack/plugins/security_solution/public/network/constants.ts diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/constants.ts b/x-pack/plugins/security_solution/public/alerts/constants.ts similarity index 65% rename from x-pack/plugins/security_solution/public/alerts/pages/detection_engine/constants.ts rename to x-pack/plugins/security_solution/public/alerts/constants.ts index e1d0c4725d7d4e..9f0b42c9c7b812 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/constants.ts +++ b/x-pack/plugins/security_solution/public/alerts/constants.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export const SINGLE_RULE_ALERTS_TABLE_ID = 'alerts-rule-details-page'; -export const ALERTS_TABLE_ID = 'detections-page-alerts'; +export const SINGLE_RULE_ALERTS_TABLE_ID = 'alerts-page-single-rule'; +export const ALERTS_TABLE_ID = 'alerts-page'; diff --git a/x-pack/plugins/security_solution/public/alerts/index.ts b/x-pack/plugins/security_solution/public/alerts/index.ts index c1501419a1cf6f..37d88e041541b9 100644 --- a/x-pack/plugins/security_solution/public/alerts/index.ts +++ b/x-pack/plugins/security_solution/public/alerts/index.ts @@ -4,15 +4,41 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; +import { getTimelineInStorageById } from '../timelines/containers/local_storage'; +import { TimelineId } from '../timelines/containers/local_storage/types'; import { getAlertsRoutes } from './routes'; import { SecuritySubPlugin } from '../app/types'; +import { SINGLE_RULE_ALERTS_TABLE_ID, ALERTS_TABLE_ID } from './constants'; + +const ALERTS_TIMELINE_IDS: TimelineId[] = [SINGLE_RULE_ALERTS_TABLE_ID, ALERTS_TABLE_ID]; export class Alerts { public setup() {} - public start(): SecuritySubPlugin { + public start(storage: Storage): SecuritySubPlugin { + const alertsTimelines = ALERTS_TIMELINE_IDS.reduce( + (acc, timelineId) => { + const timelineModel = getTimelineInStorageById(storage, timelineId); + if (!timelineModel) { + return { + ...acc, + }; + } + + return { + ...acc, + timelineById: { + ...acc.timelineById, + [timelineId]: timelineModel, + }, + }; + }, + { timelineById: {} } + ); return { routes: getAlertsRoutes(), + storageTimelines: alertsTimelines, }; } } diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx index 4ac71912ec650c..a298065f2c26d4 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx @@ -36,7 +36,7 @@ import { DetectionEngineNoIndex } from './detection_engine_no_signal_index'; import { DetectionEngineHeaderPage } from '../../components/detection_engine_header_page'; import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; -import { ALERTS_TABLE_ID } from './constants'; +import { ALERTS_TABLE_ID } from '../../constants'; export const DetectionEnginePageComponent: React.FC = ({ filters, diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx index c7a685cf46bb10..5db129c3436945 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx @@ -70,7 +70,7 @@ import { FailureHistory } from './failure_history'; import { RuleStatus } from '../../../../components/rules//rule_status'; import { useMlCapabilities } from '../../../../../common/components/ml_popover/hooks/use_ml_capabilities'; import { hasMlAdminPermissions } from '../../../../../../common/machine_learning/has_ml_admin_permissions'; -import { SINGLE_RULE_ALERTS_TABLE_ID } from '../../constants'; +import { SINGLE_RULE_ALERTS_TABLE_ID } from '../../../../constants'; enum RuleDetailTabs { alerts = 'alerts', diff --git a/x-pack/plugins/security_solution/public/hosts/constants.ts b/x-pack/plugins/security_solution/public/hosts/constants.ts new file mode 100644 index 00000000000000..e48c00b1d72698 --- /dev/null +++ b/x-pack/plugins/security_solution/public/hosts/constants.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export const EVENTS_TIMELINE_ID = 'hosts-page-events'; +export const EXTERNAL_ALERTS_TIMELINE_ID = 'hosts-page-external-alerts'; diff --git a/x-pack/plugins/security_solution/public/hosts/index.ts b/x-pack/plugins/security_solution/public/hosts/index.ts index 7c073804feb299..acbcd04f97103e 100644 --- a/x-pack/plugins/security_solution/public/hosts/index.ts +++ b/x-pack/plugins/security_solution/public/hosts/index.ts @@ -6,12 +6,13 @@ import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { SecuritySubPluginWithStore } from '../app/types'; +import { getTimelineInStorageById } from '../timelines/containers/local_storage'; +import { TimelineId } from '../timelines/containers/local_storage/types'; import { getHostsRoutes } from './routes'; import { initialHostsState, hostsReducer, HostsState } from './store'; -import { TimelineId } from '../timelines/containers/local_storage/types'; -import { getTimelineInStorageById } from '../timelines/containers/local_storage'; +import { EVENTS_TIMELINE_ID, EXTERNAL_ALERTS_TIMELINE_ID } from './constants'; -const HOST_TIMELINE_IDS: TimelineId[] = ['hosts-page-events', 'hosts-page-external-alerts']; +const HOST_TIMELINE_IDS: TimelineId[] = [EVENTS_TIMELINE_ID, EXTERNAL_ALERTS_TIMELINE_ID]; export class Hosts { public setup() {} @@ -20,6 +21,12 @@ export class Hosts { const hostTimelines = HOST_TIMELINE_IDS.reduce( (acc, timelineId) => { const timelineModel = getTimelineInStorageById(storage, timelineId); + if (!timelineModel) { + return { + ...acc, + }; + } + return { ...acc, timelineById: { diff --git a/x-pack/plugins/security_solution/public/network/constants.ts b/x-pack/plugins/security_solution/public/network/constants.ts new file mode 100644 index 00000000000000..23db79a6b48933 --- /dev/null +++ b/x-pack/plugins/security_solution/public/network/constants.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ + +export const NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID = 'network-page-external-alerts'; diff --git a/x-pack/plugins/security_solution/public/network/index.ts b/x-pack/plugins/security_solution/public/network/index.ts index 6590e5ee5161c1..d61cefe49661e2 100644 --- a/x-pack/plugins/security_solution/public/network/index.ts +++ b/x-pack/plugins/security_solution/public/network/index.ts @@ -4,16 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { SecuritySubPluginWithStore } from '../app/types'; import { getNetworkRoutes } from './routes'; import { initialNetworkState, networkReducer, NetworkState } from './store'; +import { getTimelineInStorageById } from '../timelines/containers/local_storage'; +import { NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID } from './constants'; export class Network { public setup() {} - public start(): SecuritySubPluginWithStore<'network', NetworkState> { + public start(storage: Storage): SecuritySubPluginWithStore<'network', NetworkState> { return { routes: getNetworkRoutes(), + storageTimelines: { + timelineById: { + [NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID]: + getTimelineInStorageById(storage, NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID) ?? {}, + }, + }, store: { initialState: { network: initialNetworkState }, reducer: { network: networkReducer }, diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx index daf2f6700070a9..c668f29160f3eb 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx @@ -8,10 +8,9 @@ import React from 'react'; import { Filter } from '../../../../../../../src/plugins/data/common/es_query'; import { AlertsView } from '../../../common/components/alerts_viewer'; +import { NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID } from '../../constants'; import { NetworkComponentQueryProps } from './types'; -const ALERTS_TABLE_ID = 'network-page-external-alerts'; - export const filterNetworkData: Filter[] = [ { query: { @@ -64,7 +63,11 @@ export const filterNetworkData: Filter[] = [ ]; export const NetworkAlertsQueryTabBody = React.memo((alertsProps: NetworkComponentQueryProps) => ( - + )); NetworkAlertsQueryTabBody.displayName = 'NetworkAlertsQueryTabBody'; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index b52df99892ef5b..b9eef9f799d3b9 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -70,15 +70,27 @@ export class Plugin implements IPlugin Record | null; getTimelineById: (id: TimelineId) => TimelineModel | null; From 1d636e79c0f3b207dfaffe02d1bc09cf21b68e1d Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 11 Jun 2020 11:52:41 +0300 Subject: [PATCH 38/42] Better naming --- x-pack/plugins/security_solution/public/alerts/constants.ts | 4 ++-- x-pack/plugins/security_solution/public/alerts/index.ts | 4 ++-- .../public/alerts/pages/detection_engine/detection_engine.tsx | 4 ++-- .../alerts/pages/detection_engine/rules/details/index.tsx | 4 ++-- .../security_solution/public/timelines/containers/index.tsx | 4 ++-- x-pack/test/security_solution_cypress/runner.ts | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/public/alerts/constants.ts b/x-pack/plugins/security_solution/public/alerts/constants.ts index 9f0b42c9c7b812..a2527d0f72d3ff 100644 --- a/x-pack/plugins/security_solution/public/alerts/constants.ts +++ b/x-pack/plugins/security_solution/public/alerts/constants.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export const SINGLE_RULE_ALERTS_TABLE_ID = 'alerts-page-single-rule'; -export const ALERTS_TABLE_ID = 'alerts-page'; +export const SINGLE_RULE_ALERTS_TIMELINE_ID = 'alerts-page-single-rule'; +export const ALERTS_TIMELINE_ID = 'alerts-page'; diff --git a/x-pack/plugins/security_solution/public/alerts/index.ts b/x-pack/plugins/security_solution/public/alerts/index.ts index 37d88e041541b9..091bc02c38b888 100644 --- a/x-pack/plugins/security_solution/public/alerts/index.ts +++ b/x-pack/plugins/security_solution/public/alerts/index.ts @@ -9,9 +9,9 @@ import { getTimelineInStorageById } from '../timelines/containers/local_storage' import { TimelineId } from '../timelines/containers/local_storage/types'; import { getAlertsRoutes } from './routes'; import { SecuritySubPlugin } from '../app/types'; -import { SINGLE_RULE_ALERTS_TABLE_ID, ALERTS_TABLE_ID } from './constants'; +import { SINGLE_RULE_ALERTS_TIMELINE_ID, ALERTS_TIMELINE_ID } from './constants'; -const ALERTS_TIMELINE_IDS: TimelineId[] = [SINGLE_RULE_ALERTS_TABLE_ID, ALERTS_TABLE_ID]; +const ALERTS_TIMELINE_IDS: TimelineId[] = [SINGLE_RULE_ALERTS_TIMELINE_ID, ALERTS_TIMELINE_ID]; export class Alerts { public setup() {} diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx index a298065f2c26d4..73f876f6216f38 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx @@ -36,7 +36,7 @@ import { DetectionEngineNoIndex } from './detection_engine_no_signal_index'; import { DetectionEngineHeaderPage } from '../../components/detection_engine_header_page'; import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; -import { ALERTS_TABLE_ID } from '../../constants'; +import { ALERTS_TIMELINE_ID } from '../../constants'; export const DetectionEnginePageComponent: React.FC = ({ filters, @@ -139,7 +139,7 @@ export const DetectionEnginePageComponent: React.FC = ({ /> = ({ {ruleId != null && ( { await procs.run('cypress', { cmd: 'yarn', - args: ['cypress:open'], + args: ['cypress:run'], cwd: resolve(__dirname, '../../plugins/security_solution'), env: { FORCE_COLOR: '1', From 3630cfd8fb4e99428fbedb27b00f47ce128d94d7 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 12 Jun 2020 13:18:15 +0300 Subject: [PATCH 39/42] Improve cypress tests --- .../timeline_local_storage.spec.ts | 22 +++++++++---------- .../cypress/tasks/timeline.ts | 6 ----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts index 3ea99276400b4b..a4352f58e6fc74 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_local_storage.spec.ts @@ -26,21 +26,21 @@ describe('persistent timeline', () => { }); it('persist the deletion of a column', () => { - const numberOfTimelineColumns = 9; + cy.get(DRAGGABLE_HEADER).then((header) => { + const currentNumberOfTimelineColumns = header.length; + const expectedNumberOfTimelineColumns = currentNumberOfTimelineColumns - 1; - cy.get(DRAGGABLE_HEADER) - .eq(TABLE_COLUMN_EVENTS_MESSAGE) - .invoke('text') - .should('equal', 'message'); + cy.wrap(header).eq(TABLE_COLUMN_EVENTS_MESSAGE).invoke('text').should('equal', 'message'); + removeColumn(TABLE_COLUMN_EVENTS_MESSAGE); - cy.get(DRAGGABLE_HEADER).should('have.length', numberOfTimelineColumns); + cy.get(DRAGGABLE_HEADER).should('have.length', expectedNumberOfTimelineColumns); - removeColumn(TABLE_COLUMN_EVENTS_MESSAGE); - reload(waitsForEventsToBeLoaded); + reload(waitsForEventsToBeLoaded); - cy.get(DRAGGABLE_HEADER).should('have.length', numberOfTimelineColumns - 1); - cy.get(DRAGGABLE_HEADER).each(($el) => { - expect($el.text()).not.equal('message'); + cy.get(DRAGGABLE_HEADER).should('have.length', expectedNumberOfTimelineColumns); + cy.get(DRAGGABLE_HEADER).each(($el) => { + expect($el.text()).not.equal('message'); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 90441375d12d14..a834abebcf0cc1 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -105,15 +105,9 @@ export const dragAndDropIdToggleFieldToTimeline = () => { ); }; -export const waitForTimelineActionToFinish = () => { - cy.get(TIMELINE_COLUMN_SPINNER).should('exist'); - cy.get(TIMELINE_COLUMN_SPINNER).should('not.exist'); -}; - export const removeColumn = (column: number) => { cy.get(REMOVE_COLUMN).first().should('exist'); cy.get(REMOVE_COLUMN).eq(column).click({ force: true }); - waitForTimelineActionToFinish(); }; export const resetFields = () => { From f27e8e7b0eef4d79a87ed852b2b4bba0a97f1275 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 12 Jun 2020 19:48:07 +0300 Subject: [PATCH 40/42] Fix types --- x-pack/plugins/security_solution/cypress/tasks/timeline.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index a834abebcf0cc1..9e17433090c2bb 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -23,7 +23,6 @@ import { TOGGLE_TIMELINE_EXPAND_EVENT, REMOVE_COLUMN, RESET_FIELDS, - TIMELINE_COLUMN_SPINNER, } from '../screens/timeline'; import { drag, drop } from '../tasks/common'; From 3a921654a855c453f4f1237d5a47b97bb87b30f1 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Sat, 13 Jun 2020 16:41:59 +0300 Subject: [PATCH 41/42] Review improvements --- .../components/alerts_table/index.test.tsx | 2 +- .../alerts/components/alerts_table/index.tsx | 3 +- .../public/alerts/constants.ts | 2 +- .../security_solution/public/alerts/index.ts | 32 +--- .../detection_engine/rules/details/index.tsx | 4 +- .../components/alerts_viewer/alerts_table.tsx | 3 +- .../common/components/alerts_viewer/types.ts | 3 +- .../security_solution/public/hosts/index.ts | 26 +-- .../security_solution/public/network/index.ts | 9 +- .../public/timelines/containers/index.tsx | 2 +- .../containers/local_storage/index.test.ts | 155 ++++++++++++++---- .../containers/local_storage/index.tsx | 31 +++- .../containers/local_storage/types.ts | 4 +- 13 files changed, 179 insertions(+), 97 deletions(-) diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx index 0cef13d5e80c5e..407cbcf5f9fac3 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx @@ -13,7 +13,7 @@ describe('AlertsTableComponent', () => { it('renders correctly', () => { const wrapper = shallow( { - const timelineModel = getTimelineInStorageById(storage, timelineId); - if (!timelineModel) { - return { - ...acc, - }; - } - - return { - ...acc, - timelineById: { - ...acc.timelineById, - [timelineId]: timelineModel, - }, - }; - }, - { timelineById: {} } - ); return { routes: getAlertsRoutes(), - storageTimelines: alertsTimelines, + storageTimelines: { + timelineById: getTimelinesInStorageByIds(storage, ALERTS_TIMELINE_IDS), + }, }; } } diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx index 0d709f5666a354..7046c1f5b2a129 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx @@ -74,7 +74,7 @@ import { useMlCapabilities } from '../../../../../common/components/ml_popover/h import { hasMlAdminPermissions } from '../../../../../../common/machine_learning/has_ml_admin_permissions'; import { ExceptionsViewer } from '../../../../../common/components/exceptions/viewer'; import { ExceptionListType } from '../../../../../common/components/exceptions/types'; -import { SINGLE_RULE_ALERTS_TIMELINE_ID } from '../../../../constants'; +import { ALERTS_RULES_DETAILS_PAGE_TIMELINE_ID } from '../../../../constants'; enum RuleDetailTabs { alerts = 'alerts', @@ -387,7 +387,7 @@ export const RuleDetailsPageComponent: FC = ({ {ruleId != null && ( { - timelineId: string; + timelineId: TimelineId; pageFilters: Filter[]; stackByOptions?: MatrixHistogramOption[]; defaultFilters?: Filter[]; diff --git a/x-pack/plugins/security_solution/public/hosts/index.ts b/x-pack/plugins/security_solution/public/hosts/index.ts index acbcd04f97103e..716442d1f365a3 100644 --- a/x-pack/plugins/security_solution/public/hosts/index.ts +++ b/x-pack/plugins/security_solution/public/hosts/index.ts @@ -6,7 +6,7 @@ import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { SecuritySubPluginWithStore } from '../app/types'; -import { getTimelineInStorageById } from '../timelines/containers/local_storage'; +import { getTimelinesInStorageByIds } from '../timelines/containers/local_storage'; import { TimelineId } from '../timelines/containers/local_storage/types'; import { getHostsRoutes } from './routes'; import { initialHostsState, hostsReducer, HostsState } from './store'; @@ -18,29 +18,11 @@ export class Hosts { public setup() {} public start(storage: Storage): SecuritySubPluginWithStore<'hosts', HostsState> { - const hostTimelines = HOST_TIMELINE_IDS.reduce( - (acc, timelineId) => { - const timelineModel = getTimelineInStorageById(storage, timelineId); - if (!timelineModel) { - return { - ...acc, - }; - } - - return { - ...acc, - timelineById: { - ...acc.timelineById, - [timelineId]: timelineModel, - }, - }; - }, - { timelineById: {} } - ); - return { routes: getHostsRoutes(), - storageTimelines: hostTimelines, + storageTimelines: { + timelineById: getTimelinesInStorageByIds(storage, HOST_TIMELINE_IDS), + }, store: { initialState: { hosts: initialHostsState }, reducer: { hosts: hostsReducer }, diff --git a/x-pack/plugins/security_solution/public/network/index.ts b/x-pack/plugins/security_solution/public/network/index.ts index d61cefe49661e2..3f628104e263df 100644 --- a/x-pack/plugins/security_solution/public/network/index.ts +++ b/x-pack/plugins/security_solution/public/network/index.ts @@ -8,7 +8,7 @@ import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { SecuritySubPluginWithStore } from '../app/types'; import { getNetworkRoutes } from './routes'; import { initialNetworkState, networkReducer, NetworkState } from './store'; -import { getTimelineInStorageById } from '../timelines/containers/local_storage'; +import { getTimelinesInStorageByIds } from '../timelines/containers/local_storage'; import { NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID } from './constants'; export class Network { @@ -18,10 +18,9 @@ export class Network { return { routes: getNetworkRoutes(), storageTimelines: { - timelineById: { - [NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID]: - getTimelineInStorageById(storage, NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID) ?? {}, - }, + timelineById: getTimelinesInStorageByIds(storage, [ + NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID, + ]), }, store: { initialState: { network: initialNetworkState }, diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index b1711fcfc5198c..a09f2792df159a 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -29,7 +29,7 @@ import { timelineQuery } from './index.gql_query'; import { timelineActions } from '../../timelines/store/timeline'; import { ALERTS_TIMELINE_ID as DETECTION_ENGINE_TIMELINE_ID, - SINGLE_RULE_ALERTS_TIMELINE_ID as DETECTION_ENGINE_RULES_TIMELINE_ID, + ALERTS_RULES_DETAILS_PAGE_TIMELINE_ID as DETECTION_ENGINE_RULES_TIMELINE_ID, } from '../../alerts/constants'; const timelineIds = [DETECTION_ENGINE_TIMELINE_ID, DETECTION_ENGINE_RULES_TIMELINE_ID]; diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts index 89b54181ebb4df..54055c6e2ddb8f 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LOCAL_STORAGE_TIMELINE_KEY, useTimelinesStorage } from '.'; +import { + LOCAL_STORAGE_TIMELINE_KEY, + useTimelinesStorage, + getTimelinesInStorageByIds, + getAllTimelinesInStorage, + addTimelineInStorage, +} from '.'; import { mockTimelineModel, createSecuritySolutionStorageMock } from '../../../common/mock'; import { useKibana } from '../../../common/lib/kibana'; import { createUseKibanaMock } from '../../../common/mock/kibana_react'; @@ -27,45 +33,134 @@ describe('SiemLocalStorage', () => { localStorage.clear(); }); - it('adds a timeline when storage is empty', () => { - const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'hosts-page-events': mockTimelineModel, + describe('addTimeline', () => { + it('adds a timeline when storage is empty', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ + 'hosts-page-events': mockTimelineModel, + }); + }); + + it('adds a timeline when storage contains another timelines', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ + 'hosts-page-events': mockTimelineModel, + 'hosts-page-external-alerts': mockTimelineModel, + }); }); }); - it('adds a timeline when storage contains another timelines', () => { - const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); - expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'hosts-page-events': mockTimelineModel, - 'hosts-page-external-alerts': mockTimelineModel, + describe('getAllTimelines', () => { + it('gets all timelines correctly', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + const timelines = timelineStorage.getAllTimelines(); + expect(timelines).toEqual({ + 'hosts-page-events': mockTimelineModel, + 'hosts-page-external-alerts': mockTimelineModel, + }); + }); + + it('returns an empty object if there is no timelines', () => { + const timelineStorage = useTimelinesStorage(); + const timelines = timelineStorage.getAllTimelines(); + expect(timelines).toEqual({}); }); }); - it('gets all timelines correctly', () => { - const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); - const timelines = timelineStorage.getAllTimelines(); - expect(timelines).toEqual({ - 'hosts-page-events': mockTimelineModel, - 'hosts-page-external-alerts': mockTimelineModel, + describe('getTimelineById', () => { + it('gets a timeline by id', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + const timeline = timelineStorage.getTimelineById('hosts-page-events'); + expect(timeline).toEqual(mockTimelineModel); }); }); - it('returns null if there is no timelines', () => { - const timelineStorage = useTimelinesStorage(); - const timelines = timelineStorage.getAllTimelines(); - expect(timelines).toEqual(null); + describe('getTimelinesInStorageByIds', () => { + it('gets timelines correctly', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + const timelines = getTimelinesInStorageByIds(storage, [ + 'hosts-page-events', + 'hosts-page-external-alerts', + ]); + expect(timelines).toEqual({ + 'hosts-page-events': mockTimelineModel, + 'hosts-page-external-alerts': mockTimelineModel, + }); + }); + + it('gets an empty timelime when there is no timelines', () => { + const timelines = getTimelinesInStorageByIds(storage, ['hosts-page-events']); + expect(timelines).toEqual({}); + }); + + it('returns empty timelime when there is no ids', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + const timelines = getTimelinesInStorageByIds(storage, []); + expect(timelines).toEqual({}); + }); + + it('returns empty timelime when a specific timeline does not exists', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + const timelines = getTimelinesInStorageByIds(storage, ['hosts-page-external-alerts']); + expect(timelines).toEqual({}); + }); + + it('returns timelines correctly when one exist and another not', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + const timelines = getTimelinesInStorageByIds(storage, [ + 'hosts-page-events', + 'hosts-page-external-alerts', + ]); + expect(timelines).toEqual({ + 'hosts-page-events': mockTimelineModel, + }); + }); + }); + + describe('getAllTimelinesInStorage', () => { + it('gets timelines correctly', () => { + const timelineStorage = useTimelinesStorage(); + timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + const timelines = getAllTimelinesInStorage(storage); + expect(timelines).toEqual({ + 'hosts-page-events': mockTimelineModel, + 'hosts-page-external-alerts': mockTimelineModel, + }); + }); + + it('gets an empty timeline when there is no timelines', () => { + const timelines = getAllTimelinesInStorage(storage); + expect(timelines).toEqual({}); + }); }); - it('gets a timeline by id', () => { - const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - const timeline = timelineStorage.getTimelineById('hosts-page-events'); - expect(timeline).toEqual(mockTimelineModel); + describe('addTimelineInStorage', () => { + it('adds a timeline when storage is empty', () => { + addTimelineInStorage(storage, 'hosts-page-events', mockTimelineModel); + expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ + 'hosts-page-events': mockTimelineModel, + }); + }); + + it('adds a timeline when storage contains another timelines', () => { + addTimelineInStorage(storage, 'hosts-page-events', mockTimelineModel); + addTimelineInStorage(storage, 'hosts-page-external-alerts', mockTimelineModel); + expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ + 'hosts-page-events': mockTimelineModel, + 'hosts-page-external-alerts': mockTimelineModel, + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx index ba93d32fcb1a72..8c03ae7b1a58d7 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx @@ -10,17 +10,34 @@ import { useKibana } from '../../../common/lib/kibana'; import { TimelineModel } from '../../store/timeline/model'; export const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; +const EMPTY_TIMELINE = {} as { + [K in TimelineId]: TimelineModel; +}; + +export const getTimelinesInStorageByIds = (storage: Storage, timelineIds: TimelineId[]) => { + const allTimelines = storage.get(LOCAL_STORAGE_TIMELINE_KEY); -export const getTimelineInStorageById = (storage: Storage, id: TimelineId) => { - const timelines = storage.get(LOCAL_STORAGE_TIMELINE_KEY); - if (timelines != null) { - return timelines[id]; + if (!allTimelines) { + return EMPTY_TIMELINE; } - return null; + + return timelineIds.reduce((acc, timelineId) => { + const timelineModel = allTimelines[timelineId]; + if (!timelineModel) { + return { + ...acc, + }; + } + + return { + ...acc, + [timelineId]: timelineModel, + }; + }, {} as { [K in TimelineId]: TimelineModel }); }; export const getAllTimelinesInStorage = (storage: Storage) => - storage.get(LOCAL_STORAGE_TIMELINE_KEY); + storage.get(LOCAL_STORAGE_TIMELINE_KEY) ?? {}; export const addTimelineInStorage = (storage: Storage, id: TimelineId, timeline: TimelineModel) => { const timelines = getAllTimelinesInStorage(storage); @@ -37,7 +54,7 @@ export const useTimelinesStorage = (): TimelinesStorage => { getAllTimelinesInStorage(storage); const getTimelineById: TimelinesStorage['getTimelineById'] = (id: TimelineId) => - getTimelineInStorageById(storage, id); + getTimelinesInStorageByIds(storage, [id])[id] ?? null; const addTimeline: TimelinesStorage['addTimeline'] = (id, timeline) => addTimelineInStorage(storage, id, timeline); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts index 8df845beaeddc5..d7d96c1e90abbc 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts @@ -8,7 +8,7 @@ import { TimelineModel } from '../../../timelines/store/timeline/model'; export type HOST_PAGE_EVENTS_TIMELINE_ID = 'hosts-page-events'; export type HOST_PAGE_EXTERNAL_EVENTS_TIMELINE_ID = 'hosts-page-external-alerts'; -export type ALERTS_PAGE_SINGLE_RULE_TIMELINE_ID = 'alerts-page-single-rule'; +export type ALERTS_PAGE_SINGLE_RULE_TIMELINE_ID = 'alerts-rules-details-page'; export type ALERTS_PAGE_TIMELINE_ID = 'alerts-page'; export type NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID = 'network-page-external-alerts'; @@ -19,7 +19,7 @@ export type TimelineId = | ALERTS_PAGE_TIMELINE_ID | NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID; export interface TimelinesStorage { - getAllTimelines: () => Record | null; + getAllTimelines: () => Record; getTimelineById: (id: TimelineId) => TimelineModel | null; addTimeline: (id: TimelineId, timeline: TimelineModel) => void; } From f81b1c1bae45a599d0af94c16ca4ce58db3f2367 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Mon, 15 Jun 2020 15:37:32 +0300 Subject: [PATCH 42/42] Move TimelineId to common folder --- .../common/types/timeline/index.ts | 26 +++++++ .../components/alerts_table/index.test.tsx | 3 +- .../alerts/components/alerts_table/index.tsx | 4 +- .../security_solution/public/alerts/index.ts | 9 +-- .../detection_engine/detection_engine.tsx | 4 +- .../detection_engine/rules/details/index.tsx | 4 +- .../components/alerts_viewer/alerts_table.tsx | 4 +- .../common/components/alerts_viewer/types.ts | 4 +- .../public/hosts/constants.ts | 8 -- .../security_solution/public/hosts/index.ts | 8 +- .../navigation/alerts_query_tab_body.tsx | 11 ++- .../navigation/events_query_tab_body.tsx | 4 +- .../public/network/constants.ts | 7 -- .../security_solution/public/network/index.ts | 6 +- .../navigation/alerts_query_tab_body.tsx | 4 +- .../containers/local_storage/index.test.ts | 74 ++++++++++--------- .../containers/local_storage/index.tsx | 17 +++-- .../containers/local_storage/types.ts | 19 +---- .../store/timeline/epic_local_storage.ts | 4 +- 19 files changed, 116 insertions(+), 104 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/hosts/constants.ts delete mode 100644 x-pack/plugins/security_solution/public/network/constants.ts diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index e67eb3182ffa99..4f255bb6d68341 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -197,6 +197,32 @@ export interface SavedTimeline extends runtimeTypes.TypeOf {} +/* + * Timeline IDs + */ + +export enum TimelineId { + hostsPageEvents = 'hosts-page-events', + hostsPageExternalAlerts = 'hosts-page-external-alerts', + alertsRulesDetailsPage = 'alerts-rules-details-page', + alertsPage = 'alerts-page', + networkPageExternalAlerts = 'network-page-external-alerts', + active = 'timeline-1', + test = 'test', // Reserved for testing purposes +} + +export const TimelineIdLiteralRt = runtimeTypes.union([ + runtimeTypes.literal(TimelineId.hostsPageEvents), + runtimeTypes.literal(TimelineId.hostsPageExternalAlerts), + runtimeTypes.literal(TimelineId.alertsRulesDetailsPage), + runtimeTypes.literal(TimelineId.alertsPage), + runtimeTypes.literal(TimelineId.networkPageExternalAlerts), + runtimeTypes.literal(TimelineId.active), + runtimeTypes.literal(TimelineId.test), +]); + +export type TimelineIdLiteral = runtimeTypes.TypeOf; + /** * Timeline Saved object type with metadata */ diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx index 407cbcf5f9fac3..f843bf68818465 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_table/index.test.tsx @@ -7,13 +7,14 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { TimelineId } from '../../../../common/types/timeline'; import { AlertsTableComponent } from './index'; describe('AlertsTableComponent', () => { it('renders correctly', () => { const wrapper = shallow( = ({ filters, @@ -139,7 +139,7 @@ export const DetectionEnginePageComponent: React.FC = ({ /> = ({ {ruleId != null && ( { - timelineId: TimelineId; + timelineId: TimelineIdLiteral; pageFilters: Filter[]; stackByOptions?: MatrixHistogramOption[]; defaultFilters?: Filter[]; diff --git a/x-pack/plugins/security_solution/public/hosts/constants.ts b/x-pack/plugins/security_solution/public/hosts/constants.ts deleted file mode 100644 index e48c00b1d72698..00000000000000 --- a/x-pack/plugins/security_solution/public/hosts/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * 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. - */ - -export const EVENTS_TIMELINE_ID = 'hosts-page-events'; -export const EXTERNAL_ALERTS_TIMELINE_ID = 'hosts-page-external-alerts'; diff --git a/x-pack/plugins/security_solution/public/hosts/index.ts b/x-pack/plugins/security_solution/public/hosts/index.ts index 716442d1f365a3..90d5f54a027d7f 100644 --- a/x-pack/plugins/security_solution/public/hosts/index.ts +++ b/x-pack/plugins/security_solution/public/hosts/index.ts @@ -5,14 +5,16 @@ */ import { Storage } from '../../../../../src/plugins/kibana_utils/public'; +import { TimelineIdLiteral, TimelineId } from '../../common/types/timeline'; import { SecuritySubPluginWithStore } from '../app/types'; import { getTimelinesInStorageByIds } from '../timelines/containers/local_storage'; -import { TimelineId } from '../timelines/containers/local_storage/types'; import { getHostsRoutes } from './routes'; import { initialHostsState, hostsReducer, HostsState } from './store'; -import { EVENTS_TIMELINE_ID, EXTERNAL_ALERTS_TIMELINE_ID } from './constants'; -const HOST_TIMELINE_IDS: TimelineId[] = [EVENTS_TIMELINE_ID, EXTERNAL_ALERTS_TIMELINE_ID]; +const HOST_TIMELINE_IDS: TimelineIdLiteral[] = [ + TimelineId.hostsPageEvents, + TimelineId.hostsPageExternalAlerts, +]; export class Hosts { public setup() {} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx index 7819e4fa62a2e2..3023670f8051a0 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/alerts_query_tab_body.tsx @@ -7,11 +7,10 @@ import React, { useMemo } from 'react'; import { Filter } from '../../../../../../../src/plugins/data/public'; +import { TimelineId } from '../../../../common/types/timeline'; import { AlertsView } from '../../../common/components/alerts_viewer'; import { AlertsComponentQueryProps } from './types'; -const ALERTS_TABLE_ID = 'hosts-page-external-alerts'; - export const filterHostData: Filter[] = [ { query: { @@ -50,7 +49,13 @@ export const HostAlertsQueryTabBody = React.memo((alertsProps: AlertsComponentQu [pageFilters] ); - return ; + return ( + + ); }); HostAlertsQueryTabBody.displayName = 'HostAlertsQueryTabBody'; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx index 25387386171181..574e2ec4ae250a 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/navigation/events_query_tab_body.tsx @@ -5,6 +5,7 @@ */ import React, { useEffect } from 'react'; +import { TimelineId } from '../../../../common/types/timeline'; import { StatefulEventsViewer } from '../../../common/components/events_viewer'; import { HostsComponentsQueryProps } from './types'; import { hostsModel } from '../../store'; @@ -17,7 +18,6 @@ import { MatrixHistogramContainer } from '../../../common/components/matrix_hist import * as i18n from '../translations'; import { HistogramType } from '../../../graphql/types'; -const HOSTS_PAGE_TIMELINE_ID = 'hosts-page-events'; const EVENTS_HISTOGRAM_ID = 'eventsOverTimeQuery'; export const eventsStackByOptions: MatrixHistogramOption[] = [ @@ -78,7 +78,7 @@ export const EventsQueryTabBody = ({ diff --git a/x-pack/plugins/security_solution/public/network/constants.ts b/x-pack/plugins/security_solution/public/network/constants.ts deleted file mode 100644 index 23db79a6b48933..00000000000000 --- a/x-pack/plugins/security_solution/public/network/constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * 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. - */ - -export const NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID = 'network-page-external-alerts'; diff --git a/x-pack/plugins/security_solution/public/network/index.ts b/x-pack/plugins/security_solution/public/network/index.ts index 3f628104e263df..63291ad2d2396a 100644 --- a/x-pack/plugins/security_solution/public/network/index.ts +++ b/x-pack/plugins/security_solution/public/network/index.ts @@ -8,8 +8,8 @@ import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { SecuritySubPluginWithStore } from '../app/types'; import { getNetworkRoutes } from './routes'; import { initialNetworkState, networkReducer, NetworkState } from './store'; +import { TimelineId } from '../../common/types/timeline'; import { getTimelinesInStorageByIds } from '../timelines/containers/local_storage'; -import { NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID } from './constants'; export class Network { public setup() {} @@ -18,9 +18,7 @@ export class Network { return { routes: getNetworkRoutes(), storageTimelines: { - timelineById: getTimelinesInStorageByIds(storage, [ - NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID, - ]), + timelineById: getTimelinesInStorageByIds(storage, [TimelineId.networkPageExternalAlerts]), }, store: { initialState: { network: initialNetworkState }, diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx index c668f29160f3eb..0c9f8c194bf2b4 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/alerts_query_tab_body.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { Filter } from '../../../../../../../src/plugins/data/common/es_query'; +import { TimelineId } from '../../../../common/types/timeline'; import { AlertsView } from '../../../common/components/alerts_viewer'; -import { NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID } from '../../constants'; import { NetworkComponentQueryProps } from './types'; export const filterNetworkData: Filter[] = [ @@ -64,7 +64,7 @@ export const filterNetworkData: Filter[] = [ export const NetworkAlertsQueryTabBody = React.memo((alertsProps: NetworkComponentQueryProps) => ( diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts index 54055c6e2ddb8f..e1bccbdff48899 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.test.ts @@ -11,6 +11,8 @@ import { getAllTimelinesInStorage, addTimelineInStorage, } from '.'; + +import { TimelineId } from '../../../../common/types/timeline'; import { mockTimelineModel, createSecuritySolutionStorageMock } from '../../../common/mock'; import { useKibana } from '../../../common/lib/kibana'; import { createUseKibanaMock } from '../../../common/mock/kibana_react'; @@ -36,19 +38,19 @@ describe('SiemLocalStorage', () => { describe('addTimeline', () => { it('adds a timeline when storage is empty', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'hosts-page-events': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, }); }); it('adds a timeline when storage contains another timelines', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'hosts-page-events': mockTimelineModel, - 'hosts-page-external-alerts': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, + [TimelineId.hostsPageExternalAlerts]: mockTimelineModel, }); }); }); @@ -56,12 +58,12 @@ describe('SiemLocalStorage', () => { describe('getAllTimelines', () => { it('gets all timelines correctly', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel); const timelines = timelineStorage.getAllTimelines(); expect(timelines).toEqual({ - 'hosts-page-events': mockTimelineModel, - 'hosts-page-external-alerts': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, + [TimelineId.hostsPageExternalAlerts]: mockTimelineModel, }); }); @@ -75,8 +77,8 @@ describe('SiemLocalStorage', () => { describe('getTimelineById', () => { it('gets a timeline by id', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - const timeline = timelineStorage.getTimelineById('hosts-page-events'); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); + const timeline = timelineStorage.getTimelineById(TimelineId.hostsPageEvents); expect(timeline).toEqual(mockTimelineModel); }); }); @@ -84,46 +86,46 @@ describe('SiemLocalStorage', () => { describe('getTimelinesInStorageByIds', () => { it('gets timelines correctly', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel); const timelines = getTimelinesInStorageByIds(storage, [ - 'hosts-page-events', - 'hosts-page-external-alerts', + TimelineId.hostsPageEvents, + TimelineId.hostsPageExternalAlerts, ]); expect(timelines).toEqual({ - 'hosts-page-events': mockTimelineModel, - 'hosts-page-external-alerts': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, + [TimelineId.hostsPageExternalAlerts]: mockTimelineModel, }); }); it('gets an empty timelime when there is no timelines', () => { - const timelines = getTimelinesInStorageByIds(storage, ['hosts-page-events']); + const timelines = getTimelinesInStorageByIds(storage, [TimelineId.hostsPageEvents]); expect(timelines).toEqual({}); }); it('returns empty timelime when there is no ids', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); const timelines = getTimelinesInStorageByIds(storage, []); expect(timelines).toEqual({}); }); it('returns empty timelime when a specific timeline does not exists', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - const timelines = getTimelinesInStorageByIds(storage, ['hosts-page-external-alerts']); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); + const timelines = getTimelinesInStorageByIds(storage, [TimelineId.hostsPageExternalAlerts]); expect(timelines).toEqual({}); }); it('returns timelines correctly when one exist and another not', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); const timelines = getTimelinesInStorageByIds(storage, [ - 'hosts-page-events', - 'hosts-page-external-alerts', + TimelineId.hostsPageEvents, + TimelineId.hostsPageExternalAlerts, ]); expect(timelines).toEqual({ - 'hosts-page-events': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, }); }); }); @@ -131,12 +133,12 @@ describe('SiemLocalStorage', () => { describe('getAllTimelinesInStorage', () => { it('gets timelines correctly', () => { const timelineStorage = useTimelinesStorage(); - timelineStorage.addTimeline('hosts-page-events', mockTimelineModel); - timelineStorage.addTimeline('hosts-page-external-alerts', mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageEvents, mockTimelineModel); + timelineStorage.addTimeline(TimelineId.hostsPageExternalAlerts, mockTimelineModel); const timelines = getAllTimelinesInStorage(storage); expect(timelines).toEqual({ - 'hosts-page-events': mockTimelineModel, - 'hosts-page-external-alerts': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, + [TimelineId.hostsPageExternalAlerts]: mockTimelineModel, }); }); @@ -148,18 +150,18 @@ describe('SiemLocalStorage', () => { describe('addTimelineInStorage', () => { it('adds a timeline when storage is empty', () => { - addTimelineInStorage(storage, 'hosts-page-events', mockTimelineModel); + addTimelineInStorage(storage, TimelineId.hostsPageEvents, mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'hosts-page-events': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, }); }); it('adds a timeline when storage contains another timelines', () => { - addTimelineInStorage(storage, 'hosts-page-events', mockTimelineModel); - addTimelineInStorage(storage, 'hosts-page-external-alerts', mockTimelineModel); + addTimelineInStorage(storage, TimelineId.hostsPageEvents, mockTimelineModel); + addTimelineInStorage(storage, TimelineId.hostsPageExternalAlerts, mockTimelineModel); expect(JSON.parse(localStorage.getItem(LOCAL_STORAGE_TIMELINE_KEY))).toEqual({ - 'hosts-page-events': mockTimelineModel, - 'hosts-page-external-alerts': mockTimelineModel, + [TimelineId.hostsPageEvents]: mockTimelineModel, + [TimelineId.hostsPageExternalAlerts]: mockTimelineModel, }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx index 8c03ae7b1a58d7..1a09868da77716 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/index.tsx @@ -5,16 +5,17 @@ */ import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; -import { TimelinesStorage, TimelineId } from './types'; +import { TimelinesStorage } from './types'; import { useKibana } from '../../../common/lib/kibana'; import { TimelineModel } from '../../store/timeline/model'; +import { TimelineIdLiteral } from '../../../../common/types/timeline'; export const LOCAL_STORAGE_TIMELINE_KEY = 'timelines'; const EMPTY_TIMELINE = {} as { - [K in TimelineId]: TimelineModel; + [K in TimelineIdLiteral]: TimelineModel; }; -export const getTimelinesInStorageByIds = (storage: Storage, timelineIds: TimelineId[]) => { +export const getTimelinesInStorageByIds = (storage: Storage, timelineIds: TimelineIdLiteral[]) => { const allTimelines = storage.get(LOCAL_STORAGE_TIMELINE_KEY); if (!allTimelines) { @@ -33,13 +34,17 @@ export const getTimelinesInStorageByIds = (storage: Storage, timelineIds: Timeli ...acc, [timelineId]: timelineModel, }; - }, {} as { [K in TimelineId]: TimelineModel }); + }, {} as { [K in TimelineIdLiteral]: TimelineModel }); }; export const getAllTimelinesInStorage = (storage: Storage) => storage.get(LOCAL_STORAGE_TIMELINE_KEY) ?? {}; -export const addTimelineInStorage = (storage: Storage, id: TimelineId, timeline: TimelineModel) => { +export const addTimelineInStorage = ( + storage: Storage, + id: TimelineIdLiteral, + timeline: TimelineModel +) => { const timelines = getAllTimelinesInStorage(storage); storage.set(LOCAL_STORAGE_TIMELINE_KEY, { ...timelines, @@ -53,7 +58,7 @@ export const useTimelinesStorage = (): TimelinesStorage => { const getAllTimelines: TimelinesStorage['getAllTimelines'] = () => getAllTimelinesInStorage(storage); - const getTimelineById: TimelinesStorage['getTimelineById'] = (id: TimelineId) => + const getTimelineById: TimelinesStorage['getTimelineById'] = (id: TimelineIdLiteral) => getTimelinesInStorageByIds(storage, [id])[id] ?? null; const addTimeline: TimelinesStorage['addTimeline'] = (id, timeline) => diff --git a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts index d7d96c1e90abbc..d888f3bb8b332e 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/containers/local_storage/types.ts @@ -5,21 +5,10 @@ */ import { TimelineModel } from '../../../timelines/store/timeline/model'; +import { TimelineIdLiteral } from '../../../../common/types/timeline'; -export type HOST_PAGE_EVENTS_TIMELINE_ID = 'hosts-page-events'; -export type HOST_PAGE_EXTERNAL_EVENTS_TIMELINE_ID = 'hosts-page-external-alerts'; -export type ALERTS_PAGE_SINGLE_RULE_TIMELINE_ID = 'alerts-rules-details-page'; -export type ALERTS_PAGE_TIMELINE_ID = 'alerts-page'; -export type NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID = 'network-page-external-alerts'; - -export type TimelineId = - | HOST_PAGE_EVENTS_TIMELINE_ID - | HOST_PAGE_EXTERNAL_EVENTS_TIMELINE_ID - | ALERTS_PAGE_SINGLE_RULE_TIMELINE_ID - | ALERTS_PAGE_TIMELINE_ID - | NETWORK_PAGE_EXTERNAL_EVENTS_TIMELINE_ID; export interface TimelinesStorage { - getAllTimelines: () => Record; - getTimelineById: (id: TimelineId) => TimelineModel | null; - addTimeline: (id: TimelineId, timeline: TimelineModel) => void; + getAllTimelines: () => Record; + getTimelineById: (id: TimelineIdLiteral) => TimelineModel | null; + addTimeline: (id: TimelineIdLiteral, timeline: TimelineModel) => void; } diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts index 511dc8311430ae..b3d1db23ffae8a 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.ts @@ -9,8 +9,8 @@ import { map, filter, ignoreElements, tap, withLatestFrom, delay } from 'rxjs/op import { Epic } from 'redux-observable'; import { get } from 'lodash/fp'; +import { TimelineIdLiteral } from '../../../../common/types/timeline'; import { addTimelineInStorage } from '../../containers/local_storage'; -import { TimelineId } from '../../containers/local_storage/types'; import { removeColumn, @@ -50,7 +50,7 @@ export const createTimelineLocalStorageEpic = (): Epic< tap(([action, timelineById]) => { if (timelineActionTypes.includes(action.type)) { if (storage) { - const timelineId: TimelineId = get('payload.id', action); + const timelineId: TimelineIdLiteral = get('payload.id', action); addTimelineInStorage(storage, timelineId, timelineById[timelineId]); } }