Skip to content

Commit

Permalink
[Discover] Replace DiscoverInternalStateContainer with Redux based …
Browse files Browse the repository at this point in the history
…`InternalStateStore` (#208784)

## Summary

This PR replaces Discover's current `DiscoverInternalStateContainer`
(based on Kibana's custom `ReduxLikeStateContainer`) with an actual
Redux store using Redux Toolkit. It's the first step toward migrating
all of Discover's state management to Redux as part of the Discover tabs
project.

Part of #210160.
Resolves #213304.

### Checklist

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [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
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
davismcphee and kibanamachine authored Mar 6, 2025
1 parent 989cf1e commit ccae358
Show file tree
Hide file tree
Showing 68 changed files with 1,209 additions and 876 deletions.
7 changes: 3 additions & 4 deletions examples/discover_customization_examples/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,9 @@ export class DiscoverCustomizationExamplesPlugin implements Plugin {
>();
const stateStorage = stateContainer.stateStorage;
const dataView = useObservable(
stateContainer.internalState.state$,
stateContainer.internalState.getState()
).dataView;
stateContainer.runtimeStateManager.currentDataView$,
stateContainer.runtimeStateManager.currentDataView$.getValue()
);

useEffect(() => {
if (!controlGroupAPI) {
Expand All @@ -262,7 +262,6 @@ export class DiscoverCustomizationExamplesPlugin implements Plugin {
});

const filterSubscription = controlGroupAPI.filters$.subscribe((newFilters = []) => {
stateContainer.internalState.transitions.setCustomFilters(newFilters);
stateContainer.actions.fetchData();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,16 @@ export const deepMockedFields = shallowMockedFields.map(
) as DataView['fields'];

export const buildDataViewMock = ({
name,
fields: definedFields,
id,
title,
name = 'data-view-mock',
fields: definedFields = [] as unknown as DataView['fields'],
timeFieldName,
}: {
name: string;
fields: DataView['fields'];
id?: string;
title?: string;
name?: string;
fields?: DataView['fields'];
timeFieldName?: string;
}): DataView => {
const dataViewFields = [...definedFields] as DataView['fields'];
Expand All @@ -105,9 +109,12 @@ export const buildDataViewMock = ({
return new DataViewField(spec);
};

id = id ?? `${name}-id`;
title = title ?? `${name}-title`;

const dataView = {
id: `${name}-id`,
title: `${name}-title`,
id,
title,
name,
metaFields: ['_index', '_score'],
fields: dataViewFields,
Expand All @@ -122,7 +129,7 @@ export const buildDataViewMock = ({
getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })),
isTimeNanosBased: () => false,
isPersisted: () => true,
toSpec: () => ({}),
toSpec: () => ({ id, title, name }),
toMinimalSpec: () => ({}),
getTimeField: () => {
return dataViewFields.find((field) => field.name === timeFieldName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,27 @@ import { savedSearchMockWithTimeField, savedSearchMock } from './saved_search';
import { discoverServiceMock } from './services';
import { SavedSearch } from '@kbn/saved-search-plugin/public';
import { mockCustomizationContext } from '../customizations/__mocks__/customization_context';
import {
RuntimeStateManager,
createRuntimeStateManager,
} from '../application/main/state_management/redux';

export function getDiscoverStateMock({
isTimeBased = true,
savedSearch,
runtimeStateManager,
}: {
isTimeBased?: boolean;
savedSearch?: SavedSearch;
runtimeStateManager?: RuntimeStateManager;
}) {
const history = createBrowserHistory();
history.push('/');
const container = getDiscoverStateContainer({
services: discoverServiceMock,
history,
customizationContext: mockCustomizationContext,
runtimeStateManager: runtimeStateManager ?? createRuntimeStateManager(),
});
container.savedSearchState.set(
savedSearch ? savedSearch : isTimeBased ? savedSearchMockWithTimeField : savedSearchMock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { DiscoverCustomization, DiscoverCustomizationProvider } from '../../../.
import { createCustomizationService } from '../../../../customizations/customization_service';
import { DiscoverGrid } from '../../../../components/discover_grid';
import { createDataViewDataSource } from '../../../../../common/data_sources';
import { internalStateActions } from '../../state_management/redux';

const customisationService = createCustomizationService();

Expand All @@ -46,16 +47,18 @@ async function mountComponent(fetchStatus: FetchStatus, hits: EsHitRecord[]) {
stateContainer.appState.update({
dataSource: createDataViewDataSource({ dataViewId: dataViewMock.id! }),
});
stateContainer.internalState.transitions.setDataRequestParams({
timeRangeRelative: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
timeRangeAbsolute: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
});
stateContainer.internalState.dispatch(
internalStateActions.setDataRequestParams({
timeRangeRelative: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
timeRangeAbsolute: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
})
);

stateContainer.dataState.data$.documents$ = documents$;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ import { DiscoverGridSettings } from '@kbn/saved-search-plugin/common';
import { useQuerySubscriber } from '@kbn/unified-field-list';
import { DiscoverGrid } from '../../../../components/discover_grid';
import { getDefaultRowsPerPage } from '../../../../../common/constants';
import { useInternalStateSelector } from '../../state_management/discover_internal_state_container';
import { useAppStateSelector } from '../../state_management/discover_app_state_container';
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
import { FetchStatus } from '../../../types';
Expand All @@ -73,6 +72,11 @@ import {
useAdditionalCellActions,
useProfileAccessor,
} from '../../../../context_awareness';
import {
internalStateActions,
useInternalStateDispatch,
useInternalStateSelector,
} from '../../state_management/redux';

const containerStyles = css`
position: relative;
Expand Down Expand Up @@ -108,6 +112,7 @@ function DiscoverDocumentsComponent({
onFieldEdited?: () => void;
}) {
const services = useDiscoverServices();
const dispatch = useInternalStateDispatch();
const documents$ = stateContainer.dataState.data$.documents$;
const savedSearch = useSavedSearchInitial();
const { dataViews, capabilities, uiSettings, uiActions, ebtManager, fieldsMetadata } = services;
Expand Down Expand Up @@ -204,9 +209,9 @@ function DiscoverDocumentsComponent({

const setExpandedDoc = useCallback(
(doc: DataTableRecord | undefined) => {
stateContainer.internalState.transitions.setExpandedDoc(doc);
dispatch(internalStateActions.setExpandedDoc(doc));
},
[stateContainer]
[dispatch]
);

const onResizeDataGrid = useCallback<NonNullable<UnifiedDataTableProps['onResize']>>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { DiscoverMainProvider } from '../../state_management/discover_state_prov
import { act } from 'react-dom/test-utils';
import { PanelsToggle } from '../../../../components/panels_toggle';
import { createDataViewDataSource } from '../../../../../common/data_sources';
import { RuntimeStateProvider, internalStateActions } from '../../state_management/redux';

function getStateContainer(savedSearch?: SavedSearch) {
const stateContainer = getDiscoverStateMock({ isTimeBased: true, savedSearch });
Expand All @@ -46,17 +47,19 @@ function getStateContainer(savedSearch?: SavedSearch) {

stateContainer.appState.update(appState);

stateContainer.internalState.transitions.setDataView(dataView);
stateContainer.internalState.transitions.setDataRequestParams({
timeRangeAbsolute: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
timeRangeRelative: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
});
stateContainer.internalState.dispatch(internalStateActions.setDataView(dataView));
stateContainer.internalState.dispatch(
internalStateActions.setDataRequestParams({
timeRangeAbsolute: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
timeRangeRelative: {
from: '2020-05-14T11:05:13.590',
to: '2020-05-14T11:20:13.590',
},
})
);

return stateContainer;
}
Expand Down Expand Up @@ -142,7 +145,9 @@ const mountComponent = async ({
<KibanaRenderContextProvider {...services.core}>
<KibanaContextProvider services={services}>
<DiscoverMainProvider value={stateContainer}>
<DiscoverHistogramLayout {...props} />
<RuntimeStateProvider currentDataView={dataView} adHocDataViews={[]}>
<DiscoverHistogramLayout {...props} />
</RuntimeStateProvider>
</DiscoverMainProvider>
</KibanaContextProvider>
</KibanaRenderContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { act } from 'react-dom/test-utils';
import { ErrorCallout } from '../../../../components/common/error_callout';
import { PanelsToggle } from '../../../../components/panels_toggle';
import { createDataViewDataSource } from '../../../../../common/data_sources';
import { RuntimeStateProvider, internalStateActions } from '../../state_management/redux';

jest.mock('@elastic/eui', () => ({
...jest.requireActual('@elastic/eui'),
Expand Down Expand Up @@ -104,11 +105,10 @@ async function mountComponent(
interval: 'auto',
query,
});
stateContainer.internalState.transitions.setDataView(dataView);
stateContainer.internalState.transitions.setDataRequestParams({
timeRangeAbsolute: time,
timeRangeRelative: time,
});
stateContainer.internalState.dispatch(internalStateActions.setDataView(dataView));
stateContainer.internalState.dispatch(
internalStateActions.setDataRequestParams({ timeRangeAbsolute: time, timeRangeRelative: time })
);

const props = {
dataView,
Expand All @@ -128,9 +128,11 @@ async function mountComponent(
const component = mountWithIntl(
<KibanaContextProvider services={services}>
<DiscoverMainProvider value={stateContainer}>
<EuiProvider>
<DiscoverLayout {...props} />
</EuiProvider>
<RuntimeStateProvider currentDataView={dataView} adHocDataViews={[]}>
<EuiProvider>
<DiscoverLayout {...props} />
</EuiProvider>
</RuntimeStateProvider>
</DiscoverMainProvider>
</KibanaContextProvider>,
mountOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { DiscoverGridSettings } from '@kbn/saved-search-plugin/common';
import { useSavedSearchInitial } from '../../state_management/discover_state_provider';
import { DiscoverStateContainer } from '../../state_management/discover_state';
import { VIEW_MODE } from '../../../../../common/constants';
import { useInternalStateSelector } from '../../state_management/discover_internal_state_container';
import { useAppStateSelector } from '../../state_management/discover_app_state_container';
import { useDiscoverServices } from '../../../../hooks/use_discover_services';
import { DiscoverNoResults } from '../no_results';
Expand All @@ -54,6 +53,7 @@ import { DiscoverResizableLayout } from './discover_resizable_layout';
import { PanelsToggle, PanelsToggleProps } from '../../../../components/panels_toggle';
import { sendErrorMsg } from '../../hooks/use_saved_search_messages';
import { useIsEsqlMode } from '../../hooks/use_is_esql_mode';
import { useCurrentDataView, useInternalStateSelector } from '../../state_management/redux';

const SidebarMemoized = React.memo(DiscoverSidebarResponsive);
const TopNavMemoized = React.memo(DiscoverTopNav);
Expand Down Expand Up @@ -89,7 +89,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
state.grid,
]);
const isEsqlMode = useIsEsqlMode();

const viewMode: VIEW_MODE = useAppStateSelector((state) => {
const fieldStatsNotAvailable =
!uiSettings.get(SHOW_FIELD_STATISTICS) && !!dataVisualizerService;
Expand All @@ -98,15 +97,10 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
}
return state.viewMode ?? VIEW_MODE.DOCUMENT_LEVEL;
});
const [dataView, dataViewLoading] = useInternalStateSelector((state) => [
state.dataView!,
state.isDataViewLoading,
]);
const customFilters = useInternalStateSelector((state) => state.customFilters);

const dataView = useCurrentDataView();
const dataViewLoading = useInternalStateSelector((state) => state.isDataViewLoading);
const dataState: DataMainMsg = useDataState(main$);
const savedSearch = useSavedSearchInitial();

const fetchCounter = useRef<number>(0);

useEffect(() => {
Expand Down Expand Up @@ -197,21 +191,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
[filterManager, dataView, dataViews, trackUiMetric, capabilities, ebtManager, fieldsMetadata]
);

const getOperator = (fieldName: string, values: unknown, operation: '+' | '-') => {
if (fieldName === '_exists_') {
return 'is_not_null';
}
if (values == null && operation === '-') {
return 'is_not_null';
}

if (values == null && operation === '+') {
return 'is_null';
}

return operation;
};

const onPopulateWhereClause = useCallback<DocViewFilterFn>(
(field, values, operation) => {
if (!field || !isOfAggregateQueryType(query)) {
Expand Down Expand Up @@ -430,7 +409,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
sidebarToggleState$={sidebarToggleState$}
sidebarPanel={
<SidebarMemoized
additionalFilters={customFilters}
columns={currentColumns}
documents$={stateContainer.dataState.data$.documents$}
onAddBreakdownField={canSetBreakdownField ? onAddBreakdownField : undefined}
Expand Down Expand Up @@ -503,3 +481,18 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) {
</EuiPage>
);
}

const getOperator = (fieldName: string, values: unknown, operation: '+' | '-') => {
if (fieldName === '_exists_') {
return 'is_not_null';
}
if (values == null && operation === '-') {
return 'is_not_null';
}

if (values == null && operation === '+') {
return 'is_null';
}

return operation;
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import type { InspectorAdapters } from '../../hooks/use_inspector';
import { UnifiedHistogramCustomization } from '../../../../customizations/customization_types/histogram_customization';
import { useDiscoverCustomization } from '../../../../customizations';
import { DiscoverCustomizationId } from '../../../../customizations/customization_service';
import { RuntimeStateProvider, internalStateActions } from '../../state_management/redux';
import { dataViewMockWithTimeField } from '@kbn/discover-utils/src/__mocks__';

const mockData = dataPluginMock.createStartContract();
let mockQueryState = {
Expand Down Expand Up @@ -121,7 +123,11 @@ describe('useDiscoverHistogram', () => {
};

const Wrapper = ({ children }: React.PropsWithChildren<unknown>) => (
<DiscoverMainProvider value={stateContainer}>{children as ReactElement}</DiscoverMainProvider>
<DiscoverMainProvider value={stateContainer}>
<RuntimeStateProvider currentDataView={dataViewMockWithTimeField} adHocDataViews={[]}>
{children as ReactElement}
</RuntimeStateProvider>
</DiscoverMainProvider>
);

const hook = renderHook(
Expand Down Expand Up @@ -379,15 +385,17 @@ describe('useDiscoverHistogram', () => {
expect(hook.result.current.isChartLoading).toBe(true);
});

it('should use timerange + timeRangeRelative + query given by the internalState container', async () => {
it('should use timerange + timeRangeRelative + query given by the internalState', async () => {
const fetch$ = new Subject<void>();
const stateContainer = getStateContainer();
const timeRangeAbs = { from: '2021-05-01T20:00:00Z', to: '2021-05-02T20:00:00Z' };
const timeRangeRel = { from: 'now-15m', to: 'now' };
stateContainer.internalState.transitions.setDataRequestParams({
timeRangeAbsolute: timeRangeAbs,
timeRangeRelative: timeRangeRel,
});
stateContainer.internalState.dispatch(
internalStateActions.setDataRequestParams({
timeRangeAbsolute: timeRangeAbs,
timeRangeRelative: timeRangeRel,
})
);
const { hook } = await renderUseDiscoverHistogram({ stateContainer });
act(() => {
fetch$.next();
Expand Down
Loading

0 comments on commit ccae358

Please sign in to comment.