Skip to content

Commit

Permalink
[Endpoint][SIEM] Adjust types related to subplugin's redux (#67768) (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Austin authored Jun 4, 2020
1 parent e46beca commit 2ca71b1
Show file tree
Hide file tree
Showing 59 changed files with 438 additions and 408 deletions.
21 changes: 1 addition & 20 deletions x-pack/plugins/siem/common/endpoint_alerts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,9 @@ import {
AlertEvent,
KbnConfigSchemaInputTypeOf,
AppLocation,
Immutable,
} from '../endpoint/types';

/**
* A deep readonly type that will make all children of a given object readonly recursively
*/
export type Immutable<T> = T extends undefined | null | boolean | string | number
? T
: unknown extends T
? unknown
: T extends Array<infer U>
? ImmutableArray<U>
: T extends Map<infer K, infer V>
? ImmutableMap<K, V>
: T extends Set<infer M>
? ImmutableSet<M>
: ImmutableObject<T>;

type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };

/**
* Values for the Alert APIs 'order' and 'direction' parameters.
*/
Expand Down
52 changes: 27 additions & 25 deletions x-pack/plugins/siem/public/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Reducer, AnyAction, Middleware, Dispatch } from 'redux';
import {
Reducer,
AnyAction,
Middleware,
Dispatch,
PreloadedState,
StateFromReducersMapObject,
CombinedState,
} from 'redux';

import { NavTab } from '../common/components/navigation/types';
import { HostsState } from '../hosts/store';
import { NetworkState } from '../network/store';
import { TimelineState } from '../timelines/store/timeline/types';
import { ImmutableReducer, State } from '../common/store';
import { State, SubPluginsInitReducer } from '../common/store';
import { Immutable } from '../../common/endpoint/types';
import { AlertListState } from '../../common/endpoint_alerts/types';
import { AppAction } from '../common/store/actions';
import { HostState } from '../endpoint_hosts/types';
import { ManagementState } from '../management/store/types';

export enum SiemPageName {
overview = 'overview',
Expand All @@ -38,7 +41,7 @@ export type SiemNavTabKey =
export type SiemNavTab = Record<SiemNavTabKey, NavTab>;

export interface SecuritySubPluginStore<K extends SecuritySubPluginKeyStore, T> {
initialState: Record<K, T>;
initialState: Record<K, T | undefined>;
reducer: Record<K, Reducer<T, AnyAction>>;
middleware?: Array<Middleware<{}, State, Dispatch<AppAction | Immutable<AppAction>>>>;
}
Expand All @@ -54,29 +57,28 @@ type SecuritySubPluginKeyStore =
| 'hostList'
| 'alertList'
| 'management';

/**
* Returned by the various 'SecuritySubPlugin' classes from the `start` method.
*/
export interface SecuritySubPluginWithStore<K extends SecuritySubPluginKeyStore, T>
extends SecuritySubPlugin {
store: SecuritySubPluginStore<K, T>;
}

export interface SecuritySubPlugins extends SecuritySubPlugin {
store: {
initialState: {
hosts: HostsState;
network: NetworkState;
timeline: TimelineState;
alertList: Immutable<AlertListState>;
hostList: Immutable<HostState>;
management: ManagementState;
};
reducer: {
hosts: Reducer<HostsState, AnyAction>;
network: Reducer<NetworkState, AnyAction>;
timeline: Reducer<TimelineState, AnyAction>;
alertList: ImmutableReducer<AlertListState, AppAction>;
hostList: ImmutableReducer<HostState, AppAction>;
management: ImmutableReducer<ManagementState, AppAction>;
};
initialState: PreloadedState<
CombinedState<
StateFromReducersMapObject<
/** SubPluginsInitReducer, being an interface, will not work in `StateFromReducersMapObject`.
* Picking its keys does the trick.
**/
Pick<SubPluginsInitReducer, keyof SubPluginsInitReducer>
>
>
>;
reducer: SubPluginsInitReducer;
middlewares: Array<Middleware<{}, State, Dispatch<AppAction | Immutable<AppAction>>>>;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { BrowserFields } from '../../containers/source';
import { dragAndDropModel, dragAndDropSelectors } from '../../store';
import { timelineSelectors } from '../../../timelines/store/timeline';
import { IdToDataProvider } from '../../store/drag_and_drop/model';
import { State } from '../../store/reducer';
import { State } from '../../store/types';
import { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider';
import { reArrangeProviders } from '../../../timelines/components/timeline/data_providers/helpers';
import { ACTIVE_TIMELINE_REDUX_ID } from '../top_n';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { apolloClientObservable, mockGlobalState, SUB_PLUGINS_REDUCER } from '..
import { createStore } from '../../store/store';

import { ErrorToastDispatcher } from '.';
import { State } from '../../store/reducer';
import { State } from '../../store/types';

describe('Error Toast Dispatcher', () => {
const state: State = mockGlobalState;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import { depsStartMock } from './dependencies_start_mock';
import { MiddlewareActionSpyHelper, createSpyMiddleware } from '../../store/test_utils';
import { apolloClientObservable } from '../test_providers';
import { createStore, State, substateMiddlewareFactory } from '../../store';
import { hostMiddlewareFactory } from '../../../endpoint_hosts/store';
import { alertMiddlewareFactory } from '../../../endpoint_alerts/store/middleware';
import { AppRootProvider } from './app_root_provider';
import { managementMiddlewareFactory } from '../../../management/store';
import { managementMiddlewareFactory } from '../../../management/store/middleware';
import { hostMiddlewareFactory } from '../../../endpoint_hosts/store/middleware';
import { SUB_PLUGINS_REDUCER, mockGlobalState } from '..';

type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult;
Expand Down Expand Up @@ -56,8 +56,7 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
const coreStart = coreMock.createStart({ basePath: '/mock' });
const depsStart = depsStartMock();
const middlewareSpy = createSpyMiddleware();
const state: State = mockGlobalState;
const store = createStore(state, SUB_PLUGINS_REDUCER, apolloClientObservable, [
const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, apolloClientObservable, [
substateMiddlewareFactory(
(globalState) => globalState.hostList,
hostMiddlewareFactory(coreStart, depsStart)
Expand All @@ -76,7 +75,6 @@ export const createAppRootMockRenderer = (): AppContextTestRender => {
</AppRootProvider>
);
const render: UiRender = (ui, options) => {
// @ts-ignore
return reactRender(ui, {
wrapper: AppWrapper as React.ComponentType,
...options,
Expand Down
19 changes: 11 additions & 8 deletions x-pack/plugins/siem/public/common/mock/global_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ import { networkModel } from '../../network/store';
import { TimelineType, TimelineStatus } from '../../../common/types/timeline';
import { initialAlertListState } from '../../endpoint_alerts/store/reducer';
import { initialHostListState } from '../../endpoint_hosts/store/reducer';
import { getManagementInitialState } from '../../management/store';

const alertList = initialAlertListState();
const hostList = initialHostListState();
const management = getManagementInitialState();
import { mockManagementState } from '../../management/store/reducer';
import { AlertListState } from '../../../common/endpoint_alerts/types';
import { HostState } from '../../endpoint_hosts/types';
import { ManagementState } from '../../management/types';

export const mockGlobalState: State = {
app: {
Expand Down Expand Up @@ -233,7 +232,11 @@ export const mockGlobalState: State = {
},
},
},
alertList,
hostList,
management,
/**
* These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture,
* they are cast to mutable versions here.
*/
alertList: initialAlertListState as AlertListState,
hostList: initialHostListState as HostState,
management: mockManagementState as ManagementState,
};
22 changes: 15 additions & 7 deletions x-pack/plugins/siem/public/common/mock/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@
import { hostsReducer } from '../../hosts/store';
import { networkReducer } from '../../network/store';
import { timelineReducer } from '../../timelines/store/timeline/reducer';
import { hostListReducer } from '../../endpoint_hosts/store';
import { alertListReducer } from '../../endpoint_alerts/store';
import { managementReducer } from '../../management/store';
import { managementReducer } from '../../management/store/reducer';
import { ManagementPluginReducer } from '../../management';
import { SubPluginsInitReducer } from '../store';
import { EndpointAlertsPluginReducer } from '../../endpoint_alerts';
import { EndpointHostsPluginReducer } from '../../endpoint_hosts';
import { alertListReducer } from '../../endpoint_alerts/store/reducer';
import { hostListReducer } from '../../endpoint_hosts/store/reducer';

interface Global extends NodeJS.Global {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand All @@ -18,11 +22,15 @@ interface Global extends NodeJS.Global {

export const globalNode: Global = global;

export const SUB_PLUGINS_REDUCER = {
export const SUB_PLUGINS_REDUCER: SubPluginsInitReducer = {
hosts: hostsReducer,
network: networkReducer,
timeline: timelineReducer,
hostList: hostListReducer,
alertList: alertListReducer,
management: managementReducer,
/**
* These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture,
* they are cast to mutable versions here.
*/
hostList: hostListReducer as EndpointHostsPluginReducer['hostList'],
alertList: alertListReducer as EndpointAlertsPluginReducer['alertList'],
management: managementReducer as ManagementPluginReducer['management'],
};
4 changes: 1 addition & 3 deletions x-pack/plugins/siem/public/common/store/app/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
import { keys } from 'lodash/fp';
import memoizeOne from 'memoize-one';
import { createSelector } from 'reselect';

import { Note } from '../../lib/note';
import { State } from '../reducer';

import { ErrorModel, NotesById } from './model';
import { State } from '../types';

const selectNotesById = (state: State): NotesById => state.app.notesById;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
*/

import { createSelector } from 'reselect';

import { State } from '../reducer';

import { IdToDataProvider } from './model';
import { State } from '../types';

const selectDataProviders = (state: State): IdToDataProvider => state.dragAndDrop.dataProviders;

Expand Down
38 changes: 33 additions & 5 deletions x-pack/plugins/siem/public/common/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,46 @@ export * from './model';
export * from './reducer';
export * from './selectors';

import { Middleware, Dispatch } from 'redux';
import { createStore, getStore } from './store';
import { SubstateMiddlewareFactory } from './types';
import { ImmutableMiddleware, State } from './types';
import { AppAction } from './actions';
import { Immutable } from '../../../common/endpoint/types';

export { createStore, getStore };

export const substateMiddlewareFactory: SubstateMiddlewareFactory = (selector, middleware) => {
/**
* Takes a selector and an `ImmutableMiddleware`. The
* middleware's version of `getState` will receive
* the result of the selector instead of the global state.
*
* This allows middleware to have knowledge of only a subsection of state.
*
* `selector` returns an `Immutable` version of the substate.
* `middleware` must be an `ImmutableMiddleware`.
*
* Returns a regular middleware, meant to be used with `applyMiddleware`.
*/
export const substateMiddlewareFactory = <Substate = never>(
selector: (state: State) => Substate | Immutable<Substate>,
middleware: ImmutableMiddleware<Substate, AppAction>
): Middleware<{}, State, Dispatch<AppAction | Immutable<AppAction>>> => {
return (api) => {
const substateAPI = {
...api,
// Return just the substate instead of global state.
getState() {
return selector(api.getState());
// Return the substate instead of global state.
getState(): Immutable<Substate> {
/**
* The selector will receive the basic (mutable) version of state. This is because
* the top level state shape doesn't use `Immutable`. We cast the return value as `Immutable`
* so that the middleware won't be able to mutate state.
*
* Immutable enforces nothing structural about a type so casting
* a value as `Immutable` is safe as long as nothing else is going to mutate.
* Since the state came from the return value of a reducer, the reducer will (hopefully)
* not be mutating it.
*/
return selector(api.getState()) as Immutable<Substate>;
},
};
return middleware(substateAPI);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { createSelector } from 'reselect';

import { State } from '../reducer';
import { State } from '../types';

import { InputsModel, InputsRange, GlobalQuery } from './model';

Expand Down
Loading

0 comments on commit 2ca71b1

Please sign in to comment.