Skip to content

Commit

Permalink
[SecuritySolution] Remove usage of redux observables from the localst…
Browse files Browse the repository at this point in the history
…orage and change epics (#175450)

## Summary

As a first step in removing `redux-observable` (see
#175427), this PR transforms all
localstorage epics and the timeline change epic into regular redux
middlewares.

### Checklist

Delete any items that are not applicable to this PR.

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
  • Loading branch information
janmonschke authored Jan 29, 2024
1 parent 597cfeb commit 0943f25
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 271 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -32,8 +31,6 @@ const {
upsertColumn,
} = dataTableActions;

export const isNotNull = <T>(value: T | null): value is T => value !== null;

const tableActionTypes = new Set([
removeColumn.type,
upsertColumn.type,
Expand All @@ -50,21 +47,19 @@ const tableActionTypes = new Set([
toggleDetailPanel.type,
]);

export const createDataTableLocalStorageEpic =
<State>(): Epic<Action, Action, State, TimelineEpicDependencies<State>> =>
(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;
};
Original file line number Diff line number Diff line change
@@ -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();
});
});
Loading

0 comments on commit 0943f25

Please sign in to comment.