From 0eea85e86937edd914be43ed8f6efeb038966f23 Mon Sep 17 00:00:00 2001 From: Luke G <11671118+lgestc@users.noreply.github.com> Date: Tue, 6 Aug 2024 13:39:01 +0200 Subject: [PATCH] [Security Solution] Remove experimental sourcerer (#189881) ## Summary Given that we are no longer considering replacing our sourcerer component with Discover one, I am removing the experimental flag + some boilerplate code around it. --- .../app/home/template_wrapper/index.tsx | 11 +- .../public/common/mock/global_state.ts | 2 - .../public/common/store/reducer.test.tsx | 3 - .../public/common/store/reducer.ts | 7 -- .../public/common/store/store.ts | 8 -- .../public/common/store/types.ts | 2 - .../public/sourcerer/containers/index.tsx | 9 +- .../components/dataview_picker/index.test.tsx | 94 ---------------- .../components/dataview_picker/index.tsx | 99 ----------------- .../components/dataview_picker/readme.md | 3 - .../sourcerer/experimental/constants.ts | 8 -- .../dataview_picker_provider.test.tsx | 81 -------------- .../containers/dataview_picker_provider.tsx | 46 -------- .../sourcerer/experimental/is_enabled.ts | 15 --- .../public/sourcerer/experimental/readme.md | 21 ---- .../sourcerer/experimental/redux/actions.ts | 16 --- .../experimental/redux/listeners.test.ts | 101 ----------------- .../sourcerer/experimental/redux/listeners.ts | 102 ------------------ .../sourcerer/experimental/redux/reducer.ts | 55 ---------- .../sourcerer/experimental/redux/selectors.ts | 32 ------ ...se_unstable_security_solution_data_view.ts | 38 ------- .../search_or_filter/search_or_filter.tsx | 10 +- 22 files changed, 6 insertions(+), 757 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/readme.md delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/constants.ts delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.tsx delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/is_enabled.ts delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/readme.md delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/redux/actions.ts delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.test.ts delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.ts delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/redux/reducer.ts delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/redux/selectors.ts delete mode 100644 x-pack/plugins/security_solution/public/sourcerer/experimental/use_unstable_security_solution_data_view.ts diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 5a1b8423572fcd..b6492ef97cae79 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -12,7 +12,6 @@ import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout'; -import { DataViewPickerProvider } from '../../../sourcerer/experimental/containers/dataview_picker_provider'; import { AttackDiscoveryTour } from '../../../attack_discovery/tour'; import { URL_PARAM_KEY } from '../../../common/hooks/use_url_state'; import { SecuritySolutionFlyout, TimelineFlyout } from '../../../flyout'; @@ -104,12 +103,10 @@ export const SecuritySolutionTemplateWrapper: React.FC - - - {children} - - - + + {children} + + {didMount && } diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index 8642a1eab5560f..7b399e8848c789 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -47,7 +47,6 @@ import { initialGroupingState } from '../store/grouping/reducer'; import type { SourcererState } from '../../sourcerer/store'; import { EMPTY_RESOLVER } from '../../resolver/store/helpers'; import { getMockDiscoverInTimelineState } from './mock_discover_state'; -import { initialState as dataViewPickerInitialState } from '../../sourcerer/experimental/redux/reducer'; const mockFieldMap: DataViewSpec['fields'] = Object.fromEntries( mockIndexFields.map((field) => [field.name, field]) @@ -512,7 +511,6 @@ export const mockGlobalState: State = { */ management: mockManagementState as ManagementState, discover: getMockDiscoverInTimelineState(), - dataViewPicker: dataViewPickerInitialState, notes: { entities: { '1': { diff --git a/x-pack/plugins/security_solution/public/common/store/reducer.test.tsx b/x-pack/plugins/security_solution/public/common/store/reducer.test.tsx index c04474ce5db4af..ec2143ea758543 100644 --- a/x-pack/plugins/security_solution/public/common/store/reducer.test.tsx +++ b/x-pack/plugins/security_solution/public/common/store/reducer.test.tsx @@ -13,7 +13,6 @@ import { useSourcererDataView } from '../../sourcerer/containers'; import { renderHook } from '@testing-library/react-hooks'; import { initialGroupingState } from './grouping/reducer'; import { initialAnalyzerState } from '../../resolver/store/helpers'; -import { initialState as dataViewPickerInitialState } from '../../sourcerer/experimental/redux/reducer'; import { initialNotesState } from '../../notes/store/notes.slice'; jest.mock('../hooks/use_selector'); @@ -72,7 +71,6 @@ describe('createInitialState', () => { { analyzer: initialAnalyzerState, }, - dataViewPickerInitialState, initialNotesState ); @@ -112,7 +110,6 @@ describe('createInitialState', () => { { analyzer: initialAnalyzerState, }, - dataViewPickerInitialState, initialNotesState ); const { result } = renderHook(() => useSourcererDataView(), { diff --git a/x-pack/plugins/security_solution/public/common/store/reducer.ts b/x-pack/plugins/security_solution/public/common/store/reducer.ts index 3226c508b70787..ea684909a87763 100644 --- a/x-pack/plugins/security_solution/public/common/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/reducer.ts @@ -35,10 +35,6 @@ import type { GroupState } from './grouping/types'; import { analyzerReducer } from '../../resolver/store/reducer'; import { securitySolutionDiscoverReducer } from './discover/reducer'; import type { AnalyzerState } from '../../resolver/types'; -import { - type DataviewPickerState, - reducer as dataviewPickerReducer, -} from '../../sourcerer/experimental/redux/reducer'; import type { NotesState } from '../../notes/store/notes.slice'; import { notesReducer } from '../../notes/store/notes.slice'; @@ -73,7 +69,6 @@ export const createInitialState = ( dataTableState: DataTableState, groupsState: GroupState, analyzerState: AnalyzerState, - dataviewPickerState: DataviewPickerState, notesState: NotesState ): State => { const initialPatterns = { @@ -136,7 +131,6 @@ export const createInitialState = ( internal: undefined, savedSearch: undefined, }, - dataViewPicker: dataviewPickerState, notes: notesState, }; @@ -156,7 +150,6 @@ export const createReducer: ( sourcerer: sourcererReducer, globalUrlParam: globalUrlParamReducer, dataTable: dataTableReducer, - dataViewPicker: dataviewPickerReducer, groups: groupsReducer, analyzer: analyzerReducer, discover: securitySolutionDiscoverReducer, 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 c9925f2f0a7e27..a73422ef8a4cba 100644 --- a/x-pack/plugins/security_solution/public/common/store/store.ts +++ b/x-pack/plugins/security_solution/public/common/store/store.ts @@ -55,11 +55,6 @@ import { dataAccessLayerFactory } from '../../resolver/data_access_layer/factory import { sourcererActions } from '../../sourcerer/store'; import { createMiddlewares } from './middlewares'; import { addNewTimeline } from '../../timelines/store/helpers'; -import { - reducer as dataViewPickerReducer, - initialState as dataViewPickerState, -} from '../../sourcerer/experimental/redux/reducer'; -import { listenerMiddleware } from '../../sourcerer/experimental/redux/listeners'; import { initialNotesState } from '../../notes/store/notes.slice'; let store: Store | null = null; @@ -176,7 +171,6 @@ export const createStoreFactory = async ( dataTableInitialState, groupsInitialState, analyzerInitialState, - dataViewPickerState, initialNotesState ); @@ -184,14 +178,12 @@ export const createStoreFactory = async ( ...subPlugins.explore.store.reducer, timeline: timelineReducer, ...subPlugins.management.store.reducer, - dataViewPicker: dataViewPickerReducer, }; return createStore(initialState, rootReducer, coreStart, storage, [ ...(subPlugins.management.store.middleware ?? []), ...(subPlugins.explore.store.middleware ?? []), ...[resolverMiddlewareFactory(dataAccessLayerFactory(coreStart)) ?? []], - listenerMiddleware.middleware, ]); }; diff --git a/x-pack/plugins/security_solution/public/common/store/types.ts b/x-pack/plugins/security_solution/public/common/store/types.ts index 8809ccc6ec0fa9..bf83f9146bdb22 100644 --- a/x-pack/plugins/security_solution/public/common/store/types.ts +++ b/x-pack/plugins/security_solution/public/common/store/types.ts @@ -25,7 +25,6 @@ import type { GlobalUrlParam } from './global_url_param'; import type { GroupState } from './grouping/types'; import type { SecuritySolutionDiscoverState } from './discover/model'; import type { AnalyzerState } from '../../resolver/types'; -import { type DataviewPickerState } from '../../sourcerer/experimental/redux/reducer'; import type { NotesState } from '../../notes/store/notes.slice'; export type State = HostsPluginState & @@ -39,7 +38,6 @@ export type State = HostsPluginState & sourcerer: SourcererState; globalUrlParam: GlobalUrlParam; discover: SecuritySolutionDiscoverState; - dataViewPicker: DataviewPickerState; } & DataTableState & GroupState & AnalyzerState & { notes: NotesState }; diff --git a/x-pack/plugins/security_solution/public/sourcerer/containers/index.tsx b/x-pack/plugins/security_solution/public/sourcerer/containers/index.tsx index 94a87888d2e118..a4ee0815aac975 100644 --- a/x-pack/plugins/security_solution/public/sourcerer/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/sourcerer/containers/index.tsx @@ -15,7 +15,6 @@ import { getDataViewStateFromIndexFields } from '../../common/containers/source/ import { useFetchIndex } from '../../common/containers/source'; import type { State } from '../../common/store/types'; import { sortWithExcludesAtEnd } from '../../../common/utils/sourcerer'; -import { useUnstableSecuritySolutionDataView } from '../experimental/use_unstable_security_solution_data_view'; export const useSourcererDataView = ( scopeId: SourcererScopeName = SourcererScopeName.default @@ -116,7 +115,7 @@ export const useSourcererDataView = ( return dataViewBrowserFields; }, [sourcererDataView.fields, sourcererDataView.patternList]); - const stableSourcererValues = useMemo( + return useMemo( () => ({ browserFields: browserFields(), dataViewId: sourcererDataView.id, @@ -145,10 +144,4 @@ export const useSourcererDataView = ( legacyPatterns.length, ] ); - - return useUnstableSecuritySolutionDataView( - scopeId, - // NOTE: data view derived from current implementation is used as a fallback - stableSourcererValues - ); }; diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.test.tsx b/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.test.tsx deleted file mode 100644 index 3aee25657fb0e6..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.test.tsx +++ /dev/null @@ -1,94 +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 { render, screen, fireEvent } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import { useKibana } from '../../../../common/lib/kibana/kibana_react'; -import { useDispatch, useSelector } from 'react-redux'; -import { selectDataView } from '../../redux/actions'; -import { DataViewPicker } from '.'; - -// Mock the required hooks and dependencies -jest.mock('../../../../common/lib/kibana/kibana_react', () => ({ - useKibana: jest.fn(), -})); - -jest.mock('react-redux', () => ({ - useDispatch: jest.fn(), - useSelector: jest.fn(), -})); - -jest.mock('../../redux/actions', () => ({ - selectDataView: jest.fn(), -})); - -jest.mock('@kbn/unified-search-plugin/public', () => ({ - DataViewPicker: jest.fn((props) => ( -
-
{props.trigger.label}
- - - -
- )), -})); - -describe('DataViewPicker', () => { - const mockDispatch = jest.fn(); - const mockDataViewEditor = { - openEditor: jest.fn(), - }; - const mockDataViewFieldEditor = { - openEditor: jest.fn(), - }; - const mockData = { - dataViews: { - get: jest.fn().mockResolvedValue({}), - }, - }; - - beforeEach(() => { - (useDispatch as jest.Mock).mockReturnValue(mockDispatch); - (useKibana as jest.Mock).mockReturnValue({ - services: { - dataViewEditor: mockDataViewEditor, - data: mockData, - dataViewFieldEditor: mockDataViewFieldEditor, - }, - }); - (useSelector as jest.Mock).mockReturnValue({ dataViewId: 'test-id' }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('renders the DataviewPicker component', () => { - render(); - expect(screen.getByText('Dataview')).toBeInTheDocument(); - }); - - test('calls dispatch on data view change', () => { - render(); - fireEvent.click(screen.getByText('Change DataView')); - expect(mockDispatch).toHaveBeenCalledWith(selectDataView('new-id')); - }); - - test('opens data view editor when creating a new data view', () => { - render(); - fireEvent.click(screen.getByText('Create New DataView')); - expect(mockDataViewEditor.openEditor).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.tsx b/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.tsx deleted file mode 100644 index 59bfcc9ec98eb5..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/index.tsx +++ /dev/null @@ -1,99 +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 { DataViewPicker as USDataViewPicker } from '@kbn/unified-search-plugin/public'; -import React, { useCallback, useRef, useMemo, memo } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; - -import { useKibana } from '../../../../common/lib/kibana/kibana_react'; -import { DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID } from '../../constants'; -import { selectDataView } from '../../redux/actions'; -import { sourcererAdapterSelector } from '../../redux/selectors'; - -const TRIGGER_CONFIG = { - label: 'Dataview', - color: 'danger', - title: 'Experimental data view picker', - iconType: 'beaker', -} as const; - -export const DataViewPicker = memo(() => { - const dispatch = useDispatch(); - - const { - services: { dataViewEditor, data, dataViewFieldEditor }, - } = useKibana(); - - const closeDataViewEditor = useRef<() => void | undefined>(); - const closeFieldEditor = useRef<() => void | undefined>(); - - // TODO: should this be implemented like that? If yes, we need to source dataView somehow or implement the same thing based on the existing state value. - // const canEditDataView = - // Boolean(dataViewEditor?.userPermissions.editDataView()) || !dataView.isPersisted(); - const canEditDataView = true; - - const { dataViewId } = useSelector(sourcererAdapterSelector); - - const createNewDataView = useCallback(async () => { - closeDataViewEditor.current = await dataViewEditor.openEditor({ - // eslint-disable-next-line no-console - onSave: () => console.log('new data view saved'), - allowAdHocDataView: true, - }); - }, [dataViewEditor]); - - const onFieldEdited = useCallback(() => {}, []); - - const editField = useMemo(() => { - if (!canEditDataView) { - return; - } - return async (fieldName?: string, _uiAction: 'edit' | 'add' = 'edit') => { - if (!dataViewId) { - return; - } - - const dataViewInstance = await data.dataViews.get(dataViewId); - closeFieldEditor.current = await dataViewFieldEditor.openEditor({ - ctx: { - dataView: dataViewInstance, - }, - fieldName, - onSave: async () => { - onFieldEdited(); - }, - }); - }; - }, [canEditDataView, dataViewId, data.dataViews, dataViewFieldEditor, onFieldEdited]); - - const addField = useMemo( - () => (canEditDataView && editField ? () => editField(undefined, 'add') : undefined), - [editField, canEditDataView] - ); - - const handleChangeDataView = useCallback( - (id: string) => { - dispatch(selectDataView(id)); - }, - [dispatch] - ); - - const handleEditDataView = useCallback(() => {}, []); - - return ( - - ); -}); - -DataViewPicker.displayName = 'DataviewPicker'; diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/readme.md b/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/readme.md deleted file mode 100644 index 1ce2de83ccc054..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/components/dataview_picker/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Dataview Picker - -A replacement for the Sourcerer component, based on the Discover implementation. diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/constants.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/constants.ts deleted file mode 100644 index 357e38c11c906e..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID = 'security-solution-default'; diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.test.tsx b/x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.test.tsx deleted file mode 100644 index 183d03bc02007e..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.test.tsx +++ /dev/null @@ -1,81 +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 { render } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import { useDispatch } from 'react-redux'; -import { useKibana } from '../../../common/lib/kibana'; -import { DataViewPickerProvider } from './dataview_picker_provider'; -import { - startAppListening, - listenerMiddleware, - createChangeDataviewListener, - createInitDataviewListener, -} from '../redux/listeners'; -import { init } from '../redux/actions'; -import { DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID } from '../constants'; -import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; - -jest.mock('../../../common/lib/kibana', () => ({ - useKibana: jest.fn(), -})); - -jest.mock('react-redux', () => ({ - useDispatch: jest.fn(), -})); - -jest.mock('../redux/listeners', () => ({ - listenerMiddleware: { - clearListeners: jest.fn(), - }, - startAppListening: jest.fn(), - createChangeDataviewListener: jest.fn(), - createInitDataviewListener: jest.fn(), -})); - -describe('DataviewPickerProvider', () => { - const mockDispatch = jest.fn(); - const mockServices = { - dataViews: {} as unknown as DataViewsServicePublic, - }; - - beforeEach(() => { - (useDispatch as jest.Mock).mockReturnValue(mockDispatch); - (useKibana as jest.Mock).mockReturnValue({ services: mockServices }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('starts listeners and dispatches init action on mount', () => { - render( - -
{`Test Child`}
-
- ); - - expect(startAppListening).toHaveBeenCalledWith(createInitDataviewListener({})); - expect(startAppListening).toHaveBeenCalledWith( - createChangeDataviewListener({ dataViewsService: mockServices.dataViews }) - ); - expect(mockDispatch).toHaveBeenCalledWith(init(DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID)); - }); - - test('clears listeners on unmount', () => { - const { unmount } = render( - -
{`Test Child`}
-
- ); - - unmount(); - - expect(listenerMiddleware.clearListeners).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.tsx b/x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.tsx deleted file mode 100644 index dd6d01f21e73ae..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/containers/dataview_picker_provider.tsx +++ /dev/null @@ -1,46 +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, { memo, useEffect, type FC, type PropsWithChildren } from 'react'; -import { useDispatch } from 'react-redux'; - -import { useKibana } from '../../../common/lib/kibana'; -import { - createChangeDataviewListener, - createInitDataviewListener, - listenerMiddleware, - startAppListening, -} from '../redux/listeners'; -import { init } from '../redux/actions'; -import { DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID } from '../constants'; - -// NOTE: this can be spawned multiple times, eq. when you need something like a separate data view picker for a subsection of the app - -// for example, in the timeline. -export const DataViewPickerProvider: FC> = memo(({ children }) => { - const { services } = useKibana(); - - const dispatch = useDispatch(); - - useEffect(() => { - // NOTE: the goal here is to move all side effects and business logic to Redux, - // so that we only do presentation layer things on React side - for performance reasons and - // to make the state easier to predict. - // see: https://redux-toolkit.js.org/api/createListenerMiddleware#overview - startAppListening(createInitDataviewListener({})); - startAppListening(createChangeDataviewListener({ dataViewsService: services.dataViews })); - - // NOTE: this can be dispatched at any point, with any data view id - dispatch(init(DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID)); - - // NOTE: Clear existing listeners when services change for some reason (they should not) - return () => listenerMiddleware.clearListeners(); - }, [services, dispatch]); - - return <>{children}; -}); - -DataViewPickerProvider.displayName = 'DataviewPickerProvider'; diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/is_enabled.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/is_enabled.ts deleted file mode 100644 index caf4b92c1d5d25..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/is_enabled.ts +++ /dev/null @@ -1,15 +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. - */ - -/** - * Allows toggling between sourcerer implementations in runtime. Simply set the value in local storage - * to: - * - display the experimental component instead of the stable one - * - use experimental data views hook instead of the stable one - */ -export const isExperimentalSourcererEnabled = () => - !!window.localStorage.getItem('EXPERIMENTAL_SOURCERER_ENABLED'); diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/readme.md b/x-pack/plugins/security_solution/public/sourcerer/experimental/readme.md deleted file mode 100644 index 26894153f2e902..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/readme.md +++ /dev/null @@ -1,21 +0,0 @@ -# Experimental Sourcerer Replacement - -## Introduction - -This directory is a home for Discovery Components based re-implementation of the Sourcerer. - -Currently, it can be enabled and used only by setting the localStorage value, like this: - -``` -window.localStorage.setItem('EXPERIMENTAL_SOURCERER_ENABLED', true) -``` - -The reason for having this feature toggle like this is we want to be able to inspect both implementations side by side, -using the same Kibana instance deployed locally (for now). - -## Architecture - -- Redux based -- Limited use of useEffect or stateful hooks - in favor of thunks and redux middleware (supporting request cancellation and caching) -- Allows multiple instances of the picker - just wrap the subsection of the app with its own DataviewPickerProvider -- Data exposed back to Security Solution is memoized with `reselect` for performance diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/actions.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/actions.ts deleted file mode 100644 index f1b490f1cf734f..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/actions.ts +++ /dev/null @@ -1,16 +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 { type DataViewSpec } from '@kbn/data-views-plugin/common'; -import { createAction } from '@reduxjs/toolkit'; - -type DataViewId = string; - -export const init = createAction('init'); -export const selectDataView = createAction('changeDataView'); -export const setDataViewData = createAction('setDataView'); -export const setPatternList = createAction('setPatternList'); diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.test.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.test.ts deleted file mode 100644 index 099fbb9dbcf3c7..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.test.ts +++ /dev/null @@ -1,101 +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 { - init, - selectDataView, - setDataViewData as setDataViewSpec, - setPatternList, -} from './actions'; -import { createInitDataviewListener, createChangeDataviewListener } from './listeners'; -import { isExperimentalSourcererEnabled } from '../is_enabled'; -import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; -import { type ListenerEffectAPI } from '@reduxjs/toolkit'; -import type { AppDispatch } from './listeners'; -import { type State } from '../../../common/store/types'; - -jest.mock('../is_enabled', () => ({ - isExperimentalSourcererEnabled: jest.fn().mockReturnValue(true), -})); - -type ListenerApi = ListenerEffectAPI; - -describe('Listeners', () => { - describe('createInitDataviewListener', () => { - let listenerOptions: ReturnType; - let listenerApi: ListenerApi; - - beforeEach(() => { - listenerOptions = createInitDataviewListener({}); - listenerApi = { - dispatch: jest.fn(), - getState: jest.fn(() => ({ dataViewPicker: { state: 'pristine' } })), - } as unknown as ListenerApi; - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('does not dispatch if experimental feature is disabled', async () => { - jest.mocked(isExperimentalSourcererEnabled).mockReturnValue(false); - - await listenerOptions.effect(init('test-view'), listenerApi); - expect(listenerApi.dispatch).not.toHaveBeenCalled(); - }); - - test('does not dispatch if state is not pristine', async () => { - jest.mocked(isExperimentalSourcererEnabled).mockReturnValue(true); - listenerApi.getState = jest.fn(() => ({ - dataViewPicker: { state: 'not_pristine' }, - })) as unknown as ListenerApi['getState']; - - await listenerOptions.effect(init('test-view'), listenerApi); - expect(listenerApi.dispatch).not.toHaveBeenCalled(); - }); - - test('dispatches selectDataView action if state is pristine and experimental feature is enabled', async () => { - jest.mocked(isExperimentalSourcererEnabled).mockReturnValue(true); - await listenerOptions.effect(init('test-id'), listenerApi); - expect(listenerApi.dispatch).toHaveBeenCalledWith(selectDataView('test-id')); - }); - }); - - describe('createChangeDataviewListener', () => { - let listenerOptions: ReturnType; - let listenerApi: ListenerApi; - let dataViewsServiceMock: DataViewsServicePublic; - - beforeEach(() => { - dataViewsServiceMock = { - get: jest.fn(async () => ({ - toSpec: jest.fn(() => ({ id: 'test_spec' })), - getIndexPattern: jest.fn(() => 'index_pattern'), - })), - getExistingIndices: jest.fn(async () => ['pattern1', 'pattern2']), - } as unknown as DataViewsServicePublic; - - listenerOptions = createChangeDataviewListener({ dataViewsService: dataViewsServiceMock }); - listenerApi = { - dispatch: jest.fn(), - } as unknown as ListenerApi; - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('fetches data view and dispatches setDataViewSpec and setPatternList actions', async () => { - await listenerOptions.effect(selectDataView('test_id'), listenerApi); - - expect(dataViewsServiceMock.get).toHaveBeenCalledWith('test_id', true, false); - expect(listenerApi.dispatch).toHaveBeenCalledWith(setDataViewSpec({ id: 'test_spec' })); - expect(dataViewsServiceMock.getExistingIndices).toHaveBeenCalledWith(['index_pattern']); - expect(listenerApi.dispatch).toHaveBeenCalledWith(setPatternList(['pattern1', 'pattern2'])); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.ts deleted file mode 100644 index 359794eca331ac..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/listeners.ts +++ /dev/null @@ -1,102 +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 type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; -import { - createListenerMiddleware, - type ActionCreator, - type ListenerEffectAPI, -} from '@reduxjs/toolkit'; -import type { ListenerPredicate } from '@reduxjs/toolkit/dist/listenerMiddleware/types'; -import type { Action, Store } from 'redux'; - -import { ensurePatternFormat } from '../../../../common/utils/sourcerer'; -import { isExperimentalSourcererEnabled } from '../is_enabled'; -import { - init, - selectDataView, - setDataViewData as setDataViewSpec, - setPatternList, -} from './actions'; -import { type State } from '../../../common/store/types'; - -export type AppDispatch = Store['dispatch']; - -export type DatapickerActions = ReturnType; - -// NOTE: types below exist because we are using redux-toolkit version lower than 2.x -// in v2, there are TS helpers that make it easy to setup overrides that are necessary here. -export interface ListenerOptions { - // Match with a function accepting action and state. This is broken in v1.x, - // the predicate is always required - predicate?: ListenerPredicate; - // Match action by type - type?: string; - // Exact action type match based on the RTK action creator - actionCreator?: ActionCreator; - // An effect to call - effect: (action: DatapickerActions, api: ListenerEffectAPI) => Promise; -} - -/** - * This is the proposed way of handling side effects within sourcerer code. We will no longer rely on useEffect for doing things like - * enriching the store with data fetched asynchronously in response to user doing something. - * Thunks are also considered for simpler flows but this has the advantage of cancellation support through `listnerApi` below. - */ - -export type ListenerCreator = ( - // Only specify a subset of required services here, so that it is easier to mock and therefore test the listener - dependencies: TDependencies -) => ListenerOptions; - -// NOTE: this should only be executed once in the application lifecycle, to LAZILY setup the component data -export const createInitDataviewListener: ListenerCreator<{}> = (): ListenerOptions => { - return { - actionCreator: init, - effect: async (action, listenerApi) => { - // WARN: Skip the init call if the experimental implementation is disabled - if (!isExperimentalSourcererEnabled()) { - return; - } - // NOTE: We should only run this once, when particular sourcerer instance is in pristine state (not touched by the user) - if (listenerApi.getState().dataViewPicker.state !== 'pristine') { - return; - } - - // NOTE: dispatch the regular change listener - listenerApi.dispatch(selectDataView(action.payload)); - }, - }; -}; - -// NOTE: this listener is executed whenever user decides to select dataview from the picker -export const createChangeDataviewListener: ListenerCreator<{ - dataViewsService: DataViewsServicePublic; -}> = ({ dataViewsService }): ListenerOptions => { - return { - actionCreator: selectDataView, - effect: async (action, listenerApi) => { - const dataViewId = action.payload; - const refreshFields = false; - - const dataView = await dataViewsService.get(dataViewId, true, refreshFields); - const dataViewData = dataView.toSpec(); - listenerApi.dispatch(setDataViewSpec(dataViewData)); - - const defaultPatternsList = ensurePatternFormat(dataView.getIndexPattern().split(',')); - const patternList = await dataViewsService.getExistingIndices(defaultPatternsList); - listenerApi.dispatch(setPatternList(patternList)); - }, - }; -}; - -export const listenerMiddleware = createListenerMiddleware(); - -// NOTE: register side effect listeners -export const startAppListening = listenerMiddleware.startListening as unknown as ( - options: ListenerOptions -) => void; diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/reducer.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/reducer.ts deleted file mode 100644 index d8d626a1141ced..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/reducer.ts +++ /dev/null @@ -1,55 +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 type { DataViewSpec } from '@kbn/data-views-plugin/common'; -import { createReducer } from '@reduxjs/toolkit'; - -import { DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID } from '../constants'; - -import { selectDataView, setDataViewData, setPatternList } from './actions'; - -export interface SelectedDataViewState { - dataView: DataViewSpec; - patternList: string[]; - /** - * There are several states the picker can be in internally: - * - pristine - not initialized yet - * - loading - * - error - some kind of a problem during data init - * - ready - ready to provide index information to the client - */ - state: 'pristine' | 'loading' | 'error' | 'ready'; -} - -export const initialDataView: DataViewSpec = { - id: DEFAULT_SECURITY_SOLUTION_DATA_VIEW_ID, - title: '', - fields: {}, -}; - -export const initialState: SelectedDataViewState = { - dataView: initialDataView, - state: 'pristine', - patternList: [], -}; - -export const reducer = createReducer(initialState, (builder) => { - builder.addCase(selectDataView, (state) => { - state.state = 'loading'; - }); - - builder.addCase(setDataViewData, (state, action) => { - state.dataView = action.payload; - }); - - builder.addCase(setPatternList, (state, action) => { - state.patternList = action.payload; - state.state = 'ready'; - }); -}); - -export type DataviewPickerState = ReturnType; diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/selectors.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/selectors.ts deleted file mode 100644 index cdfc3a9882b0d7..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/redux/selectors.ts +++ /dev/null @@ -1,32 +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 { createSelector } from '@reduxjs/toolkit'; -import type { SelectedDataView } from '../../store/model'; -import { type State } from '../../../common/store/types'; - -/** - * Compatibility layer / adapter for legacy selector consumers. - * It is used in useSecuritySolutionDataView hook as alternative data source (behind a flag) - */ -export const sourcererAdapterSelector = createSelector( - [(state: State) => state.dataViewPicker], - (dataViewPicker): SelectedDataView => { - return { - loading: dataViewPicker.state === 'loading', - dataViewId: dataViewPicker.dataView.id || '', - patternList: dataViewPicker.patternList, - indicesExist: true, - browserFields: {}, - activePatterns: dataViewPicker.patternList, - runtimeMappings: {}, - selectedPatterns: dataViewPicker.patternList, - indexPattern: { fields: [], title: dataViewPicker.dataView.title || '' }, - sourcererDataView: {}, - }; - } -); diff --git a/x-pack/plugins/security_solution/public/sourcerer/experimental/use_unstable_security_solution_data_view.ts b/x-pack/plugins/security_solution/public/sourcerer/experimental/use_unstable_security_solution_data_view.ts deleted file mode 100644 index 4bca209b8e50a8..00000000000000 --- a/x-pack/plugins/security_solution/public/sourcerer/experimental/use_unstable_security_solution_data_view.ts +++ /dev/null @@ -1,38 +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 { useMemo } from 'react'; -import { useSelector } from 'react-redux'; - -import { type SourcererScopeName, type SelectedDataView } from '../store/model'; - -import { isExperimentalSourcererEnabled } from './is_enabled'; -import { sourcererAdapterSelector } from './redux/selectors'; - -/** - * WARN: FOR INTERNAL USE ONLY - * This hook provides data for experimental Sourcerer replacement in Security Solution. - * Do not use in client code as the API will change frequently. - * It will be extended in the future, covering more and more functionality from the current sourcerer. - */ -export const useUnstableSecuritySolutionDataView = ( - _scopeId: SourcererScopeName, - fallbackDataView: SelectedDataView -): SelectedDataView => { - const dataView: SelectedDataView = useSelector(sourcererAdapterSelector); - - const dataViewWithFallbacks: SelectedDataView = useMemo(() => { - return { - ...dataView, - // NOTE: temporary values sourced from the fallback. Will be replaced in the near future. - browserFields: fallbackDataView.browserFields, - sourcererDataView: fallbackDataView.sourcererDataView, - }; - }, [dataView, fallbackDataView.browserFields, fallbackDataView.sourcererDataView]); - - return isExperimentalSourcererEnabled() ? dataViewWithFallbacks : fallbackDataView; -}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx index f694c473d84478..bd5adf6b38cc41 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx @@ -11,8 +11,6 @@ import styled from 'styled-components'; import type { Filter } from '@kbn/es-query'; import type { FilterManager } from '@kbn/data-plugin/public'; -import { DataViewPicker } from '../../../../sourcerer/experimental/components/dataview_picker'; -import { isExperimentalSourcererEnabled } from '../../../../sourcerer/experimental/is_enabled'; import { type TimelineType, TimelineTypeEnum } from '../../../../../common/api/timeline'; import { InputsModelId } from '../../../../common/store/inputs/constants'; import type { KqlMode } from '../../../store/model'; @@ -105,12 +103,6 @@ export const SearchOrFilter = React.memo( [isDataProviderEmpty, isDataProviderVisible] ); - const dataviewPicker = isExperimentalSourcererEnabled() ? ( - - ) : ( - - ); - return ( <> @@ -121,7 +113,7 @@ export const SearchOrFilter = React.memo( responsive={false} > - {dataviewPicker} +