diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.test.tsx
deleted file mode 100644
index d7ea246e90109..0000000000000
--- a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.test.tsx
+++ /dev/null
@@ -1,185 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React from 'react';
-import { shallow } from 'enzyme';
-
-// we don't have the types for waitFor just yet, so using "as waitFor" for when we do
-import { waitFor } from '@testing-library/react';
-import '../../mock/match_media';
-import {
- mockGlobalState,
- SUB_PLUGINS_REDUCER,
- TestProviders,
- defaultHeaders,
- createSecuritySolutionStorageMock,
- kibanaObservable,
-} from '../../mock';
-
-import type { State } from '..';
-import { createStore } from '..';
-import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
-import type { EventsViewerProps } from '../../components/events_viewer';
-import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
-
-import { addTableInStorage } from '../../../timelines/containers/local_storage';
-import { Direction } from '../../../../common/search_strategy';
-import { StatefulEventsViewer } from '../../components/events_viewer';
-import { eventsDefaultModel } from '../../components/events_viewer/default_model';
-import { EntityType } from '@kbn/timelines-plugin/common';
-import { getDefaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
-import { SourcererScopeName } from '../sourcerer/model';
-import { TableId, dataTableActions } from '@kbn/securitysolution-data-table';
-
-const {
- applyDeltaToColumnWidth,
- removeColumn,
- updateColumnOrder,
- updateColumns,
- updateColumnWidth,
- updateItemsPerPage,
- updateSort,
- upsertColumn,
-} = dataTableActions;
-
-jest.mock('../../../timelines/containers/local_storage');
-
-const addTableInStorageMock = addTableInStorage as jest.Mock;
-
-describe('epicLocalStorage', () => {
- const state: State = mockGlobalState;
- const { storage } = createSecuritySolutionStorageMock();
- let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
-
- let testProps = {} as EventsViewerProps;
-
- beforeEach(() => {
- store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
- const from = '2019-08-27T22:10:56.794Z';
- const to = '2019-08-26T22:10:56.791Z';
- const ACTION_BUTTON_COUNT = 4;
-
- testProps = {
- defaultModel: eventsDefaultModel,
- end: to,
- entityType: EntityType.EVENTS,
- tableId: TableId.test,
- leadingControlColumns: getDefaultControlColumn(ACTION_BUTTON_COUNT),
- renderCellValue: DefaultCellRenderer,
- rowRenderers: defaultRowRenderers,
- sourcererScope: SourcererScopeName.default,
- start: from,
- bulkActions: false,
- hasCrudPermissions: true,
- };
- });
-
- it('persist adding / reordering of a column correctly', async () => {
- shallow(
-
-
-
- );
- store.dispatch(upsertColumn({ id: TableId.test, index: 1, column: defaultHeaders[0] }));
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-
- it('persist timeline when removing a column ', async () => {
- shallow(
-
-
-
- );
- store.dispatch(removeColumn({ id: TableId.test, columnId: '@timestamp' }));
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-
- it('persists resizing of a column', async () => {
- shallow(
-
-
-
- );
- store.dispatch(
- applyDeltaToColumnWidth({ id: TableId.test, columnId: '@timestamp', delta: 80 })
- );
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-
- it('persist the resetting of the fields', async () => {
- shallow(
-
-
-
- );
- store.dispatch(updateColumns({ id: TableId.test, columns: defaultHeaders }));
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-
- it('persist items per page', async () => {
- shallow(
-
-
-
- );
- store.dispatch(updateItemsPerPage({ id: TableId.test, itemsPerPage: 50 }));
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-
- it('persist the sorting of a column', async () => {
- shallow(
-
-
-
- );
- store.dispatch(
- updateSort({
- id: TableId.test,
- sort: [
- {
- columnId: 'event.severity',
- columnType: 'number',
- esTypes: ['long'],
- sortDirection: Direction.desc,
- },
- ],
- })
- );
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-
- it('persists updates to the column order to local storage', async () => {
- shallow(
-
-
-
- );
- store.dispatch(
- updateColumnOrder({
- columnIds: ['event.severity', '@timestamp', 'event.category'],
- id: TableId.test,
- })
- );
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-
- it('persists updates to the column width to local storage', async () => {
- shallow(
-
-
-
- );
- store.dispatch(
- updateColumnWidth({
- columnId: 'event.severity',
- id: TableId.test,
- width: 123,
- })
- );
- await waitFor(() => expect(addTableInStorageMock).toHaveBeenCalled());
- });
-});
diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts b/x-pack/plugins/security_solution/public/common/store/data_table/middleware_local_storage.ts
similarity index 53%
rename from x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts
rename to x-pack/plugins/security_solution/public/common/store/data_table/middleware_local_storage.ts
index f84b29341b712..0d8b63f8c283a 100644
--- a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts
+++ b/x-pack/plugins/security_solution/public/common/store/data_table/middleware_local_storage.ts
@@ -5,17 +5,16 @@
* 2.0.
*/
-import type { Action } from 'redux';
-import { map, filter, ignoreElements, tap, withLatestFrom, delay } from 'rxjs/operators';
-import type { Epic } from 'redux-observable';
+import type { Action, Middleware } from 'redux';
import { get } from 'lodash/fp';
-import { dataTableActions } from '@kbn/securitysolution-data-table';
+import type { Storage } from '@kbn/kibana-utils-plugin/public';
+import { dataTableActions, dataTableSelectors } from '@kbn/securitysolution-data-table';
import type { TableIdLiteral } from '@kbn/securitysolution-data-table';
import { updateTotalCount } from '../../../timelines/store/actions';
import { addTableInStorage } from '../../../timelines/containers/local_storage';
-import type { TimelineEpicDependencies } from '../../../timelines/store/types';
+import type { State } from '../types';
const {
applyDeltaToColumnWidth,
@@ -32,8 +31,6 @@ const {
upsertColumn,
} = dataTableActions;
-export const isNotNull = (value: T | null): value is T => value !== null;
-
const tableActionTypes = new Set([
removeColumn.type,
upsertColumn.type,
@@ -50,21 +47,19 @@ const tableActionTypes = new Set([
toggleDetailPanel.type,
]);
-export const createDataTableLocalStorageEpic =
- (): Epic> =>
- (action$, state$, { tableByIdSelector, storage }) => {
- const table$ = state$.pipe(map(tableByIdSelector), filter(isNotNull));
- return action$.pipe(
- delay(500),
- withLatestFrom(table$),
- tap(([action, tableById]) => {
- if (tableActionTypes.has(action.type)) {
- if (storage) {
- const tableId: TableIdLiteral = get('payload.id', action);
- addTableInStorage(storage, tableId, tableById[tableId]);
- }
- }
- }),
- ignoreElements()
- );
+export const dataTableLocalStorageMiddleware: (storage: Storage) => Middleware<{}, State> =
+ (storage: Storage) => (store) => (next) => (action: Action) => {
+ // perform the action
+ const ret = next(action);
+
+ // persist the data table state when a table action has been performed
+ if (tableActionTypes.has(action.type)) {
+ const tableById = dataTableSelectors.tableByIdSelector(store.getState());
+ const tableId: TableIdLiteral = get('payload.id', action);
+ if (tableById && tableById[tableId] && storage) {
+ addTableInStorage(storage, tableId, tableById[tableId]);
+ }
+ }
+
+ return ret;
};
diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/middlware_local_storage.test.ts b/x-pack/plugins/security_solution/public/common/store/data_table/middlware_local_storage.test.ts
new file mode 100644
index 0000000000000..251d40527e79f
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/store/data_table/middlware_local_storage.test.ts
@@ -0,0 +1,123 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import '../../mock/match_media';
+import {
+ mockGlobalState,
+ SUB_PLUGINS_REDUCER,
+ defaultHeaders,
+ createSecuritySolutionStorageMock,
+ kibanaObservable,
+} from '../../mock';
+
+import type { State } from '..';
+import { createStore } from '..';
+
+import { addTableInStorage } from '../../../timelines/containers/local_storage';
+import { Direction } from '../../../../common/search_strategy';
+import { TableId, dataTableActions } from '@kbn/securitysolution-data-table';
+
+const {
+ applyDeltaToColumnWidth,
+ removeColumn,
+ updateColumnOrder,
+ updateColumns,
+ updateColumnWidth,
+ updateItemsPerPage,
+ updateSort,
+ upsertColumn,
+} = dataTableActions;
+
+jest.mock('../../../timelines/containers/local_storage');
+
+const addTableInStorageMock = addTableInStorage as jest.Mock;
+
+describe('DataTable localStorage middleware', () => {
+ const state: State = mockGlobalState;
+ const { storage } = createSecuritySolutionStorageMock();
+ let store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
+
+ beforeEach(() => {
+ store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
+ });
+
+ it('should call the storage method with the most recent table state', () => {
+ store.dispatch(updateItemsPerPage({ id: TableId.test, itemsPerPage: 42 }));
+ expect(addTableInStorageMock).toHaveBeenCalledWith(
+ storage,
+ TableId.test,
+ expect.objectContaining({
+ itemsPerPage: 42,
+ })
+ );
+ });
+
+ it('persist adding / reordering of a column correctly', () => {
+ store.dispatch(upsertColumn({ id: TableId.test, index: 1, column: defaultHeaders[0] }));
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+
+ it('persist timeline when removing a column ', async () => {
+ store.dispatch(removeColumn({ id: TableId.test, columnId: '@timestamp' }));
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+
+ it('persists resizing of a column', async () => {
+ store.dispatch(
+ applyDeltaToColumnWidth({ id: TableId.test, columnId: '@timestamp', delta: 80 })
+ );
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+
+ it('persist the resetting of the fields', async () => {
+ store.dispatch(updateColumns({ id: TableId.test, columns: defaultHeaders }));
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+
+ it('persist items per page', async () => {
+ store.dispatch(updateItemsPerPage({ id: TableId.test, itemsPerPage: 50 }));
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+
+ it('persist the sorting of a column', async () => {
+ store.dispatch(
+ updateSort({
+ id: TableId.test,
+ sort: [
+ {
+ columnId: 'event.severity',
+ columnType: 'number',
+ esTypes: ['long'],
+ sortDirection: Direction.desc,
+ },
+ ],
+ })
+ );
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+
+ it('persists updates to the column order to local storage', async () => {
+ store.dispatch(
+ updateColumnOrder({
+ columnIds: ['event.severity', '@timestamp', 'event.category'],
+ id: TableId.test,
+ })
+ );
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+
+ it('persists updates to the column width to local storage', async () => {
+ store.dispatch(
+ updateColumnWidth({
+ columnId: 'event.severity',
+ id: TableId.test,
+ width: 123,
+ })
+ );
+ expect(addTableInStorageMock).toHaveBeenCalled();
+ });
+});
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 20311d6c4a163..a5cebee7c96b6 100644
--- a/x-pack/plugins/security_solution/public/common/store/epic.ts
+++ b/x-pack/plugins/security_solution/public/common/store/epic.ts
@@ -9,21 +9,16 @@ import type { Epic } from 'redux-observable';
import { combineEpics } from 'redux-observable';
import type { Action } from 'redux';
import type { Observable } from 'rxjs';
-import type { Storage } from '@kbn/kibana-utils-plugin/public';
import type { CoreStart } from '@kbn/core/public';
import { createTimelineEpic } from '../../timelines/store/epic';
-import { createTimelineChangedEpic } from '../../timelines/store/epic_changed';
import { createTimelineFavoriteEpic } from '../../timelines/store/epic_favorite';
import { createTimelineNoteEpic } from '../../timelines/store/epic_note';
import { createTimelinePinnedEventEpic } from '../../timelines/store/epic_pinned_event';
import type { TimelineEpicDependencies } from '../../timelines/store/types';
-import { createDataTableLocalStorageEpic } from './data_table/epic_local_storage';
-import { createUserAssetTableLocalStorageEpic } from '../../explore/users/store/epic_storage';
import type { State } from './types';
export interface RootEpicDependencies {
kibana$: Observable;
- storage: Storage;
}
export const createRootEpic = (): Epic<
@@ -34,10 +29,7 @@ export const createRootEpic = (): Epic<
> =>
combineEpics(
createTimelineEpic(),
- createTimelineChangedEpic(),
createTimelineFavoriteEpic(),
createTimelineNoteEpic(),
- createTimelinePinnedEventEpic(),
- createDataTableLocalStorageEpic(),
- createUserAssetTableLocalStorageEpic()
+ createTimelinePinnedEventEpic()
);
diff --git a/x-pack/plugins/security_solution/public/common/store/middlewares.ts b/x-pack/plugins/security_solution/public/common/store/middlewares.ts
new file mode 100644
index 0000000000000..f65cc2abe0a53
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/store/middlewares.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import type { Storage } from '@kbn/kibana-utils-plugin/public';
+
+import { createTimelineMiddlewares } from '../../timelines/store/middlewares/create_timeline_middlewares';
+import { dataTableLocalStorageMiddleware } from './data_table/middleware_local_storage';
+import { userAssetTableLocalStorageMiddleware } from '../../explore/users/store/middleware_storage';
+
+export function createMiddlewares(storage: Storage) {
+ return [
+ dataTableLocalStorageMiddleware(storage),
+ userAssetTableLocalStorageMiddleware(storage),
+ ...createTimelineMiddlewares(),
+ ];
+}
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 b9f29867a32b5..bcdd7b67b3b44 100644
--- a/x-pack/plugins/security_solution/public/common/store/store.ts
+++ b/x-pack/plugins/security_solution/public/common/store/store.ts
@@ -24,7 +24,6 @@ import { BehaviorSubject, pluck } from 'rxjs';
import type { Storage } from '@kbn/kibana-utils-plugin/public';
import type { CoreStart } from '@kbn/core/public';
import reduceReducers from 'reduce-reducers';
-import { dataTableSelectors } from '@kbn/securitysolution-data-table';
import { initialGroupingState } from './grouping/reducer';
import type { GroupState } from './grouping/types';
import {
@@ -55,6 +54,7 @@ import type { AnalyzerState } from '../../resolver/types';
import { resolverMiddlewareFactory } from '../../resolver/store/middleware';
import { dataAccessLayerFactory } from '../../resolver/data_access_layer/factory';
import { sourcererActions } from './sourcerer';
+import { createMiddlewares } from './middlewares';
let store: Store | null = null;
@@ -278,8 +278,6 @@ export const createStore = (
selectNotesByIdSelector: appSelectors.selectNotesByIdSelector,
timelineByIdSelector: timelineSelectors.timelineByIdSelector,
timelineTimeRangeSelector: inputsSelectors.timelineTimeRangeSelector,
- tableByIdSelector: dataTableSelectors.tableByIdSelector,
- storage,
};
const epicMiddleware = createEpicMiddleware(
@@ -289,6 +287,7 @@ export const createStore = (
);
const middlewareEnhancer = applyMiddleware(
+ ...createMiddlewares(storage),
epicMiddleware,
telemetryMiddleware,
...(additionalMiddleware ?? [])
diff --git a/x-pack/plugins/security_solution/public/explore/users/store/epic_storage.test.tsx b/x-pack/plugins/security_solution/public/explore/users/store/middleware_storage.test.ts
similarity index 70%
rename from x-pack/plugins/security_solution/public/explore/users/store/epic_storage.test.tsx
rename to x-pack/plugins/security_solution/public/explore/users/store/middleware_storage.test.ts
index 71274cf172197..a38c6b7e0ea1c 100644
--- a/x-pack/plugins/security_solution/public/explore/users/store/epic_storage.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/store/middleware_storage.test.ts
@@ -5,7 +5,6 @@
* 2.0.
*/
-import { waitFor } from '@testing-library/react';
import {
createSecuritySolutionStorageMock,
kibanaObservable,
@@ -21,9 +20,8 @@ import type { Store } from 'redux';
let store: Store;
const storage = createSecuritySolutionStorageMock().storage;
-describe('UsersAssetTable EpicStorage', () => {
+describe('UsersAssetTable localStorage middleware', () => {
beforeEach(() => {
- jest.useFakeTimers();
storage.clear();
store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage);
});
@@ -33,22 +31,16 @@ describe('UsersAssetTable EpicStorage', () => {
// Add field to the table
store.dispatch(addUserAssetTableField({ tableId: UserAssetTableType.assetEntra, fieldName }));
- await waitFor(() => {
- return expect(getUserAssetTableFromStorage(storage)).toEqual({
- [UserAssetTableType.assetEntra]: { fields: [fieldName] },
- });
+ expect(getUserAssetTableFromStorage(storage)).toEqual({
+ [UserAssetTableType.assetEntra]: { fields: [fieldName] },
});
- jest.runAllTimers(); // pass the time to ensure that the state is persisted to local storage
-
// Remove field from the table
store.dispatch(
removeUserAssetTableField({ tableId: UserAssetTableType.assetEntra, fieldName })
);
- await waitFor(() => {
- return expect(getUserAssetTableFromStorage(storage)).toEqual({
- [UserAssetTableType.assetEntra]: { fields: [] },
- });
+ expect(getUserAssetTableFromStorage(storage)).toEqual({
+ [UserAssetTableType.assetEntra]: { fields: [] },
});
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/users/store/epic_storage.ts b/x-pack/plugins/security_solution/public/explore/users/store/middleware_storage.ts
similarity index 51%
rename from x-pack/plugins/security_solution/public/explore/users/store/epic_storage.ts
rename to x-pack/plugins/security_solution/public/explore/users/store/middleware_storage.ts
index 97935588cd844..80cbd1e43f79a 100644
--- a/x-pack/plugins/security_solution/public/explore/users/store/epic_storage.ts
+++ b/x-pack/plugins/security_solution/public/explore/users/store/middleware_storage.ts
@@ -5,34 +5,34 @@
* 2.0.
*/
-import type { Action } from 'redux';
-import { map, filter, ignoreElements, tap, withLatestFrom, delay } from 'rxjs/operators';
-import type { Epic } from 'redux-observable';
+import type { Action, Middleware } from 'redux';
import { get } from 'lodash/fp';
-import type { RootEpicDependencies } from '../../../common/store/epic';
+import type { Storage } from '@kbn/kibana-utils-plugin/public';
+
import { usersActions } from '.';
import { selectUserAssetTables } from './selectors';
import type { UserAssetTableType } from './model';
import type { State } from '../../../common/store/types';
-export const isNotNull = (value: T | null): value is T => value !== null;
import { persistUserAssetTableInStorage } from './storage';
const { removeUserAssetTableField, addUserAssetTableField } = usersActions;
const tableActionTypes = new Set([removeUserAssetTableField.type, addUserAssetTableField.type]);
-export const createUserAssetTableLocalStorageEpic =
- (): Epic =>
- (action$, state$, { storage }) => {
- const table$ = state$.pipe(map(selectUserAssetTables), filter(isNotNull));
- return action$.pipe(
- delay(500),
- withLatestFrom(table$),
- tap(([action, tableById]) => {
+export const userAssetTableLocalStorageMiddleware: (storage: Storage) => Middleware<{}, State> =
+ (storage: Storage) => (store) => (next) => (action: Action) => {
+ // perform the action
+ const ret = next(action);
+
+ // persist the table state when a table action has been performed
+ if (tableActionTypes.has(action.type)) {
+ const tableById = selectUserAssetTables(store.getState());
+ const tableId: UserAssetTableType = get('payload.tableId', action);
+ if (tableById && tableById[tableId] && storage) {
if (tableActionTypes.has(action.type)) {
- const tableId: UserAssetTableType = get('payload.tableId', action);
persistUserAssetTableInStorage(storage, tableId, tableById[tableId]);
}
- }),
- ignoreElements()
- );
+ }
+ }
+
+ return ret;
};
diff --git a/x-pack/plugins/security_solution/public/timelines/store/epic_changed.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/changed.ts
similarity index 70%
rename from x-pack/plugins/security_solution/public/timelines/store/epic_changed.ts
rename to x-pack/plugins/security_solution/public/timelines/store/middlewares/changed.ts
index ed4294207b308..aac6fa71b4467 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/epic_changed.ts
+++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/changed.ts
@@ -5,10 +5,8 @@
* 2.0.
*/
-import type { Action } from 'redux';
-import type { Epic } from 'redux-observable';
+import type { Action, Middleware } from 'redux';
import { get } from 'lodash/fp';
-import { filter, map } from 'rxjs/operators';
import {
applyKqlFilterQuery,
@@ -33,7 +31,7 @@ import {
setSavedQueryId,
setChanged,
updateSavedSearch,
-} from './actions';
+} from '../actions';
/**
* All action types that will mark a timeline as changed
@@ -65,19 +63,26 @@ const timelineChangedTypes = new Set([
]);
/**
- * Maps actions that mark a timeline change to `setChanged` actions.
+ * Emit actions that will mark a timeline change to `setChanged` actions.
* This allows to detect unsaved timeline changes when navigating away from the timeline.
*/
-export const createTimelineChangedEpic = (): Epic => (action$) => {
- return action$.pipe(
- // Only apply mapping to some actions
- filter((action) => timelineChangedTypes.has(action.type)),
- // Map the action to a `changed` action
- map((action) =>
- setChanged({
- id: get('payload.id', action) as string,
- changed: true,
- })
- )
- );
-};
+export const timelineChangedMiddleware: Middleware =
+ ({ dispatch }) =>
+ (next) =>
+ (action: Action) => {
+ // perform the action
+ const ret = next(action);
+
+ // if the action matches one of the "change" actions,
+ // dispatch a `setChanged` action on top
+ if (timelineChangedTypes.has(action.type)) {
+ dispatch(
+ setChanged({
+ id: get('payload.id', action) as string,
+ changed: true,
+ })
+ );
+ }
+
+ return ret;
+ };
diff --git a/x-pack/plugins/security_solution/public/timelines/store/middlewares/create_timeline_middlewares.ts b/x-pack/plugins/security_solution/public/timelines/store/middlewares/create_timeline_middlewares.ts
new file mode 100644
index 0000000000000..80f320b755c7e
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/timelines/store/middlewares/create_timeline_middlewares.ts
@@ -0,0 +1,12 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { timelineChangedMiddleware } from './changed';
+
+export function createTimelineMiddlewares() {
+ return [timelineChangedMiddleware];
+}
diff --git a/x-pack/plugins/security_solution/public/timelines/store/types.ts b/x-pack/plugins/security_solution/public/timelines/store/types.ts
index 741b906dcdebe..fa8a915ddb35d 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/types.ts
+++ b/x-pack/plugins/security_solution/public/timelines/store/types.ts
@@ -6,7 +6,6 @@
*/
import type { FilterManager } from '@kbn/data-plugin/public';
-import type { TableById } from '@kbn/securitysolution-data-table';
import type { RootEpicDependencies } from '../../common/store/epic';
import type { ColumnHeaderOptions, SortColumnTimeline } from '../../../common/types';
import type { RowRendererId } from '../../../common/api/timeline';
@@ -41,7 +40,6 @@ export interface TimelineEpicDependencies extends RootEpicDependencies {
timelineTimeRangeSelector: (state: State) => inputsModel.TimeRange;
selectAllTimelineQuery: () => (state: State, id: string) => inputsModel.GlobalQuery;
selectNotesByIdSelector: (state: State) => NotesById;
- tableByIdSelector: (state: State) => TableById;
}
export interface TimelineModelSettings {