diff --git a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/index.tsx index 0aeaee799014c..85b7edf9c45de 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/index.tsx @@ -17,6 +17,7 @@ import type { ChangeEventHandler } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; +import type { DataViewSpec } from '@kbn/data-views-plugin/common'; import * as i18n from './translations'; import type { sourcererModel } from '../store'; import { sourcererActions, sourcererSelectors } from '../store'; @@ -52,12 +53,7 @@ interface SourcererPopoverProps { signalIndexName: string | null; handleClosePopOver: () => void; isTimelineSourcerer: boolean; - selectedDataViewId: string | null; - sourcererMissingPatterns: string[]; - onUpdateDetectionAlertsChecked: () => void; handleOutsideClick: () => void; - setMissingPatterns: (missingPatterns: string[]) => void; - setDataViewId: (dataViewId: string | null) => void; scopeId: sourcererModel.SourcererScopeName; children: React.ReactNode; } @@ -76,11 +72,6 @@ const SourcererPopover = React.memo( signalIndexName, handleClosePopOver, isTimelineSourcerer, - selectedDataViewId, - sourcererMissingPatterns, - onUpdateDetectionAlertsChecked, - setMissingPatterns, - setDataViewId, scopeId, children, }) => { @@ -230,7 +221,9 @@ export const Sourcerer = React.memo(({ scope: scopeId } ( newSelectedDataView: string, newSelectedPatterns: string[], - shouldValidateSelectedPatterns?: boolean + shouldValidateSelectedPatterns?: boolean, + // Overrides data view entirely with a specified one. Only works with shouldValidateSelectedPatterns set to false. + dataView?: DataViewSpec ) => { dispatch( sourcererActions.setSelectedDataView({ @@ -238,6 +231,7 @@ export const Sourcerer = React.memo(({ scope: scopeId } selectedDataViewId: newSelectedDataView, selectedPatterns: newSelectedPatterns, shouldValidateSelectedPatterns, + dataView, }) ); @@ -296,9 +290,13 @@ export const Sourcerer = React.memo(({ scope: scopeId } }, [resetDataSources]); const handleApplyFallbackDataView = useCallback( - (newSelectedDataViewId: string, patterns: string[], shouldValidate?: boolean) => { - dispatchChangeDataView(newSelectedDataViewId, patterns, false); - setDataViewId(newSelectedDataViewId); + (dataView: DataViewSpec) => { + if (!dataView.id) { + return; + } + + dispatchChangeDataView(dataView.id, dataView.title?.split(',') || [], false, dataView); + setDataViewId(dataView.id); }, [dispatchChangeDataView] ); @@ -318,7 +316,8 @@ export const Sourcerer = React.memo(({ scope: scopeId } enableFallback: // NOTE: there are cases where sourcerer does not know the dataViewId to display and only knows required patterns. // In that case, we enable the fallback dataview creation that will try to create adhoc data view based on the patterns & will select it. - (dataViewId === null && isModified === 'deprecated') || isModified === 'missingPatterns', + (!loading && dataViewId === null && isModified === 'deprecated') || + isModified === 'missingPatterns', missingPatterns, onResolveErrorManually: handleDataViewFallbackError, }); @@ -352,11 +351,6 @@ export const Sourcerer = React.memo(({ scope: scopeId } selectedPatterns={selectedPatterns} signalIndexName={signalIndexName} handleClosePopOver={handleClosePopOver} - selectedDataViewId={selectedDataViewId} - sourcererMissingPatterns={sourcererMissingPatterns} - onUpdateDetectionAlertsChecked={onUpdateDetectionAlertsChecked} - setMissingPatterns={setMissingPatterns} - setDataViewId={setDataViewId} scopeId={scopeId} > diff --git a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/use_data_view_fallback.ts b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/use_data_view_fallback.ts index b36732b8e866b..5fdf64a673b49 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/use_data_view_fallback.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/components/use_data_view_fallback.ts @@ -6,6 +6,7 @@ */ import { useEffect, useMemo } from 'react'; +import type { DataViewSpec } from '@kbn/data-views-plugin/common'; import { useCreateAdhocDataView } from './use_create_adhoc_data_view'; interface UseDataViewFallbackConfig { @@ -27,11 +28,7 @@ interface UseDataViewFallbackConfig { /** * Calls external function on dataview change */ - onApplyFallbackDataView: ( - newSelectedDataView: string, - newSelectedPatterns: string[], - shouldValidateSelectedPatterns?: boolean - ) => void; + onApplyFallbackDataView: (dataView: DataViewSpec) => void; } export const useDataViewFallback = ({ @@ -70,9 +67,7 @@ export const useDataViewFallback = ({ return; } - const patterns = adhocDataView.getIndexPattern().split(','); - - onApplyFallbackDataView(adhocDataView.id, patterns, false); + onApplyFallbackDataView(adhocDataView.toSpec()); })(); return () => { diff --git a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/containers/index.tsx b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/containers/index.tsx index 2ad961fac3c42..06148b0151ab7 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/containers/index.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/containers/index.tsx @@ -5,15 +5,13 @@ * 2.0. */ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useMemo } from 'react'; import { useSelector } from 'react-redux'; -import type { FieldSpec } from '@kbn/data-plugin/common'; import { sourcererSelectors } from '../store'; -import type { SelectedDataView, SourcererDataView, RunTimeMappings } from '../store/model'; +import type { SelectedDataView } from '../store/model'; import { SourcererScopeName } from '../store/model'; import { checkIfIndicesExist } from '../store/helpers'; import { getDataViewStateFromIndexFields } from '../../common/containers/source/use_data_view'; -import { useFetchIndex } from '../../common/containers/source'; import type { State } from '../../common/store/types'; import { sortWithExcludesAtEnd } from '../../../common/utils/sourcerer'; @@ -35,56 +33,22 @@ export const useSourcererDataView = ( const scopeSelectedPatterns = useSelector((state: State) => { return sourcererSelectors.sourcererScopeSelectedPatterns(state, scopeId); }); - const missingPatterns = useSelector((state: State) => { - return sourcererSelectors.sourcererScopeMissingPatterns(state, scopeId); - }); const selectedPatterns = useMemo( () => sortWithExcludesAtEnd(scopeSelectedPatterns), [scopeSelectedPatterns] ); - const [legacyPatterns, setLegacyPatterns] = useState([]); - - const [indexPatternsLoading, fetchIndexReturn] = useFetchIndex(legacyPatterns); - - const legacyDataView: Omit & { id: string | null } = useMemo( - () => ({ - ...fetchIndexReturn, - dataView: fetchIndexReturn.dataView, - runtimeMappings: (fetchIndexReturn.dataView?.runtimeFieldMap as RunTimeMappings) ?? {}, - title: fetchIndexReturn.dataView?.title ?? '', - id: fetchIndexReturn.dataView?.id ?? null, - loading: indexPatternsLoading, - patternList: fetchIndexReturn.indexes, - indexFields: fetchIndexReturn.indexPatterns.fields as FieldSpec[], - fields: fetchIndexReturn.dataView?.fields, - }), - [fetchIndexReturn, indexPatternsLoading] - ); - - useEffect(() => { - if (selectedDataView == null || missingPatterns.length > 0) { - // old way of fetching indices, legacy timeline - setLegacyPatterns(selectedPatterns); - } else { - setLegacyPatterns([]); - } - }, [missingPatterns, selectedDataView, selectedPatterns]); - const sourcererDataView = useMemo(() => { - const _dv = - selectedDataView == null || missingPatterns.length > 0 ? legacyDataView : selectedDataView; - // Make sure the title is up to date, so that the correct index patterns are used everywhere return { - ..._dv, + ...selectedDataView, dataView: { - ..._dv.dataView, + ...selectedDataView?.dataView, title: selectedPatterns.join(','), - name: selectedPatterns.join(','), + name: selectedDataView?.dataView?.name ?? selectedPatterns.join(','), }, }; - }, [legacyDataView, missingPatterns.length, selectedDataView, selectedPatterns]); + }, [selectedDataView, selectedPatterns]); const indicesExist = useMemo(() => { if (loading || sourcererDataView.loading) { @@ -93,7 +57,7 @@ export const useSourcererDataView = ( return checkIfIndicesExist({ scopeId, signalIndexName, - patternList: sourcererDataView.patternList, + patternList: sourcererDataView.patternList ?? [], isDefaultDataViewSelected: sourcererDataView.id === defaultDataView.id, }); } @@ -109,7 +73,7 @@ export const useSourcererDataView = ( const browserFields = useCallback(() => { const { browserFields: dataViewBrowserFields } = getDataViewStateFromIndexFields( - sourcererDataView.patternList.join(','), + sourcererDataView?.patternList?.join(',') || '', sourcererDataView.fields ); return dataViewBrowserFields; @@ -118,9 +82,9 @@ export const useSourcererDataView = ( return useMemo( () => ({ browserFields: browserFields(), - dataViewId: sourcererDataView.id, + dataViewId: sourcererDataView.id ?? null, indicesExist, - loading: loading || sourcererDataView.loading, + loading: loading || !!sourcererDataView?.loading, // selected patterns in DATA_VIEW including filter selectedPatterns, sourcererDataView: sourcererDataView.dataView, diff --git a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/actions.ts b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/actions.ts index 54aa7dfa2fef8..fb6b0a6fdbe74 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/actions.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/actions.ts @@ -7,6 +7,7 @@ import actionCreatorFactory from 'typescript-fsa'; +import type { DataViewSpec } from '@kbn/data-views-plugin/common'; import type { SelectedDataView, SourcererDataView, SourcererScopeName } from './model'; import type { SecurityDataView } from '../containers/create_sourcerer_data_view'; @@ -35,5 +36,6 @@ export interface SelectedDataViewPayload { selectedDataViewId: SelectedDataView['dataViewId']; selectedPatterns: SelectedDataView['selectedPatterns']; shouldValidateSelectedPatterns?: boolean; + dataView?: DataViewSpec; } export const setSelectedDataView = actionCreator('SET_SELECTED_DATA_VIEW'); diff --git a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/helpers.ts b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/helpers.ts index 8b51de411ff01..da8c4195e9f30 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/helpers.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/sourcerer/store/helpers.ts @@ -52,7 +52,7 @@ export const validateSelectedPatterns = ( payload: SelectedDataViewPayload, shouldValidateSelectedPatterns: boolean ): Partial => { - const { id, ...rest } = payload; + const { id, dataView: dataViewOverride, ...rest } = payload; const dataView = state.kibanaDataViews.find((p) => p.id === rest.selectedDataViewId); // dedupe because these could come from a silly url or pre 8.0 timeline const dedupePatterns = ensurePatternFormat(rest.selectedPatterns); @@ -90,11 +90,19 @@ export const validateSelectedPatterns = ( const signalIndexName = state.signalIndexName; selectedPatterns = getPatternListFromScope(id, selectedPatterns, signalIndexName); + let selectedDataViewId = dataView?.id ?? null; + + if (dataViewOverride) { + selectedPatterns = payload.selectedPatterns; + missingPatterns = []; + selectedDataViewId = String(dataViewOverride.id); + } + return { [id]: { ...state.sourcererScopes[id], ...rest, - selectedDataViewId: dataView?.id ?? null, + selectedDataViewId, selectedPatterns, missingPatterns, // if in timeline, allow for empty in case pattern was deleted