[8.8] [Security Solution] Improve rules exception flyout opening for the indices with huge amount of fields (#159216) (#159801)

# Backport

This will backport the following commits from `main` to `8.8`:
- [[Security Solution] Improve rules exception flyout opening for the
indices with huge amount of fields
(#159216)](https://github.com/elastic/kibana/pull/159216)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Ievgen
Sorokopud","email":"ievgen.sorokopud@elastic.co"},"sourceCommit":{"committedDate":"2023-06-15T12:57:15Z","message":"[Security
Solution] Improve rules exception flyout opening for the indices with
huge amount of fields (#159216)\n\n## Summary\r\n\r\nOriginal
ticket:\r\n[#158751](https://github.com/elastic/kibana/issues/158751)\r\n\r\nThese
changes improve the rule's exceptions flyout opening experience.\r\nWe
had a few complaints that it is very slow to open it and sometimes
it\r\nthrows an exception about the limited response size.\r\n\r\nTo fix
this, we decided to load extended field's data (conflicts
and\r\nunmapped info) only when user selects some field instead of
fetching\r\nthis data for all fields on flyout opening.\r\n\r\n##
NOTES:\r\n\r\nAfter these changes we gonna do next steps related to
fields loading\r\nwhen user creates/edits rule exceptions:\r\n1. We will
call `_fields_for_wildcard` **WITHOUT**\r\n`include_unmapped=true`
parameter to fetch all fields specs on exception\r\nflyout loading\r\n2.
We will call `_fields_for_wildcard` **WITH**
`include_unmapped=true`\r\nfor only one field when user selects it from
the dropdown menu\r\n\r\nWith these changes we will improve slow
exception flyout opening when\r\nuser has lots of fields which are
unmapped in different indices. If for\r\nsome reason user has a lot of
(thousands) conflicting fields around\r\nindices then the loading is
still might be slow as the\r\n`_fields_for_wildcard` call will return
conflicts information even\r\nwithout `include_unmapped=true`
parameter.\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"31b34771c5e6f710858a7f617bbca04537cf5c1b","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Team:
SecuritySolution","ci:cloud-deploy","v8.9.0","Team:Detection
Engine","v8.8.2"],"number":159216,"url":"https://github.com/elastic/kibana/pull/159216","mergeCommit":{"message":"[Security
Solution] Improve rules exception flyout opening for the indices with
huge amount of fields (#159216)\n\n## Summary\r\n\r\nOriginal
ticket:\r\n[#158751](https://github.com/elastic/kibana/issues/158751)\r\n\r\nThese
changes improve the rule's exceptions flyout opening experience.\r\nWe
had a few complaints that it is very slow to open it and sometimes
it\r\nthrows an exception about the limited response size.\r\n\r\nTo fix
this, we decided to load extended field's data (conflicts
and\r\nunmapped info) only when user selects some field instead of
fetching\r\nthis data for all fields on flyout opening.\r\n\r\n##
NOTES:\r\n\r\nAfter these changes we gonna do next steps related to
fields loading\r\nwhen user creates/edits rule exceptions:\r\n1. We will
call `_fields_for_wildcard` **WITHOUT**\r\n`include_unmapped=true`
parameter to fetch all fields specs on exception\r\nflyout loading\r\n2.
We will call `_fields_for_wildcard` **WITH**
`include_unmapped=true`\r\nfor only one field when user selects it from
the dropdown menu\r\n\r\nWith these changes we will improve slow
exception flyout opening when\r\nuser has lots of fields which are
unmapped in different indices. If for\r\nsome reason user has a lot of
(thousands) conflicting fields around\r\nindices then the loading is
still might be slow as the\r\n`_fields_for_wildcard` call will return
conflicts information even\r\nwithout `include_unmapped=true`
parameter.\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"31b34771c5e6f710858a7f617bbca04537cf5c1b"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/159216","number":159216,"mergeCommit":{"message":"[Security
Solution] Improve rules exception flyout opening for the indices with
huge amount of fields (#159216)\n\n## Summary\r\n\r\nOriginal
ticket:\r\n[#158751](https://github.com/elastic/kibana/issues/158751)\r\n\r\nThese
changes improve the rule's exceptions flyout opening experience.\r\nWe
had a few complaints that it is very slow to open it and sometimes
it\r\nthrows an exception about the limited response size.\r\n\r\nTo fix
this, we decided to load extended field's data (conflicts
and\r\nunmapped info) only when user selects some field instead of
fetching\r\nthis data for all fields on flyout opening.\r\n\r\n##
NOTES:\r\n\r\nAfter these changes we gonna do next steps related to
fields loading\r\nwhen user creates/edits rule exceptions:\r\n1. We will
call `_fields_for_wildcard` **WITHOUT**\r\n`include_unmapped=true`
parameter to fetch all fields specs on exception\r\nflyout loading\r\n2.
We will call `_fields_for_wildcard` **WITH**
`include_unmapped=true`\r\nfor only one field when user selects it from
the dropdown menu\r\n\r\nWith these changes we will improve slow
exception flyout opening when\r\nuser has lots of fields which are
unmapped in different indices. If for\r\nsome reason user has a lot of
(thousands) conflicting fields around\r\nindices then the loading is
still might be slow as the\r\n`_fields_for_wildcard` call will return
conflicts information even\r\nwithout `include_unmapped=true`
parameter.\r\n\r\n---------\r\n\r\nCo-authored-by: Kibana Machine
<42973632+kibanamachine@users.noreply.github.com>","sha":"31b34771c5e6f710858a7f617bbca04537cf5c1b"}},{"branch":"8.8","label":"v8.8.2","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Ievgen Sorokopud <ievgen.sorokopud@elastic.co>
This commit is contained in:
Kibana Machine 2023-06-15 12:10:22 -04:00 committed by GitHub
parent f3f2d31cc0
commit 608652a0a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 107 additions and 66 deletions

View file

@ -197,14 +197,15 @@ describe('BuilderEntryItem', () => {
);
});
test('it render mapping issues warning text when field has mapping conflicts', () => {
test('it render mapping issues warning text when field has mapping conflicts', async () => {
const field = getField('mapping issues');
wrapper = mount(
<BuilderEntryItem
autocompleteService={autocompleteStartMock}
entry={{
correspondingKeywordField: undefined,
entryIndex: 0,
field: getField('mapping issues'),
field,
id: '123',
nested: undefined,
operator: isOperator,
@ -223,12 +224,16 @@ describe('BuilderEntryItem', () => {
setWarningsExist={jest.fn()}
showLabel
allowCustomOptions
getExtendedFields={(): Promise<FieldSpec[]> => Promise.resolve([field])}
/>
);
expect(wrapper.find('.euiFormHelpText.euiFormRow__text').text()).toMatch(
/This field is defined as different types across the following indices or is unmapped. This can cause unexpected query results./
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('.euiFormHelpText.euiFormRow__text').text()).toMatch(
/This field is defined as different types across the following indices or is unmapped. This can cause unexpected query results./
);
});
});
test('it renders field values correctly when operator is "isOperator"', () => {

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import {
EuiAccordion,
@ -26,6 +26,7 @@ import {
} from '@kbn/securitysolution-io-ts-list-types';
import {
BuilderEntry,
DataViewField,
EXCEPTION_OPERATORS_ONLY_LISTS,
FormattedBuilderEntry,
OperatorOption,
@ -87,6 +88,7 @@ export interface EntryItemProps {
isDisabled?: boolean;
operatorsList?: OperatorOption[];
allowCustomOptions?: boolean;
getExtendedFields?: (fields: string[]) => Promise<DataViewField[]>;
}
export const BuilderEntryItem: React.FC<EntryItemProps> = ({
@ -106,6 +108,7 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
isDisabled = false,
operatorsList,
allowCustomOptions = false,
getExtendedFields,
}): JSX.Element => {
const sPaddingSize = useEuiPaddingSize('s');
@ -175,6 +178,22 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
[onChange, entry]
);
const [extendedField, setExtendedField] = useState<DataViewField | null>(null);
useEffect(() => {
if (!entry.field?.name) {
setExtendedField(null);
}
const fetchExtendedField = async (): Promise<void> => {
const fieldName = entry.field?.name;
if (getExtendedFields && fieldName) {
const extendedFields = await getExtendedFields([fieldName]);
const field = extendedFields.find((f) => f.name === fieldName) ?? null;
setExtendedField(field);
}
};
fetchExtendedField();
}, [entry.field?.name, getExtendedFields]);
const isFieldComponentDisabled = useMemo(
(): boolean =>
isDisabled ||
@ -212,8 +231,11 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
);
const warningIconCss = { marginRight: `${sPaddingSize}` };
const getMappingConflictsWarning = (field: DataViewFieldBase): React.ReactNode | null => {
const conflictsInfo = getMappingConflictsInfo(field);
const getMappingConflictsWarning = (): React.ReactNode | null => {
if (!extendedField) {
return null;
}
const conflictsInfo = getMappingConflictsInfo(extendedField);
if (!conflictsInfo) {
return null;
}
@ -238,13 +260,13 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
data-test-subj="mappingConflictsAccordion"
>
<div data-test-subj="mappingConflictsDescription">
{conflictsInfo.map((info) => {
{conflictsInfo.map((info, idx) => {
const groupDetails = info.groupedIndices.map(
({ name, count }) =>
`${count > 1 ? i18n.CONFLICT_MULTIPLE_INDEX_DESCRIPTION(name, count) : name}`
);
return (
<>
<EuiFlexItem key={`${idx}`}>
<EuiSpacer size="s" />
{`${
info.totalIndexCount > 1
@ -254,7 +276,7 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
)
: info.type
}: ${groupDetails.join(', ')}`}
</>
</EuiFlexItem>
);
})}
<EuiSpacer size="s" />
@ -268,12 +290,12 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
entry.nested == null && allowCustomOptions ? i18n.CUSTOM_COMBOBOX_OPTION_TEXT : undefined;
const helpText =
entry.field?.conflictDescriptions == null ? (
extendedField?.conflictDescriptions == null ? (
customOptionText
) : (
<>
{customOptionText}
{getMappingConflictsWarning(entry.field)}
{getMappingConflictsWarning()}
</>
);
return (
@ -295,8 +317,9 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
osTypes,
isDisabled,
handleFieldChange,
sPaddingSize,
allowCustomOptions,
sPaddingSize,
extendedField,
]
);

View file

@ -13,6 +13,7 @@ import { HttpStart } from '@kbn/core/public';
import { ExceptionListType, OsTypeArray } from '@kbn/securitysolution-io-ts-list-types';
import {
BuilderEntry,
DataViewField,
ExceptionsBuilderExceptionItem,
FormattedBuilderEntry,
OperatorOption,
@ -65,6 +66,7 @@ interface BuilderExceptionListItemProps {
isDisabled?: boolean;
operatorsList?: OperatorOption[];
allowCustomOptions?: boolean;
getExtendedFields?: (fields: string[]) => Promise<DataViewField[]>;
}
export const BuilderExceptionListItemComponent = React.memo<BuilderExceptionListItemProps>(
@ -88,6 +90,7 @@ export const BuilderExceptionListItemComponent = React.memo<BuilderExceptionList
isDisabled = false,
operatorsList,
allowCustomOptions = false,
getExtendedFields,
}) => {
const handleEntryChange = useCallback(
(entry: BuilderEntry, entryIndex: number): void => {
@ -161,6 +164,7 @@ export const BuilderExceptionListItemComponent = React.memo<BuilderExceptionList
}
operatorsList={operatorsList}
allowCustomOptions={allowCustomOptions}
getExtendedFields={getExtendedFields}
/>
</MyOverflowContainer>
<BuilderEntryDeleteButtonComponent

View file

@ -22,6 +22,7 @@ import {
} from '@kbn/securitysolution-io-ts-list-types';
import {
CreateExceptionListItemBuilderSchema,
DataViewField,
ExceptionsBuilderExceptionItem,
ExceptionsBuilderReturnExceptionItem,
OperatorOption,
@ -98,6 +99,7 @@ export interface ExceptionBuilderProps {
operatorsList?: OperatorOption[];
exceptionItemName?: string;
allowCustomFieldOptions?: boolean;
getExtendedFields?: (fields: string[]) => Promise<DataViewField[]>;
}
export const ExceptionBuilderComponent = ({
@ -121,6 +123,7 @@ export const ExceptionBuilderComponent = ({
osTypes,
operatorsList,
allowCustomFieldOptions = false,
getExtendedFields,
}: ExceptionBuilderProps): JSX.Element => {
const [state, dispatch] = useReducer(exceptionsBuilderReducer(), {
...initialState,
@ -442,6 +445,7 @@ export const ExceptionBuilderComponent = ({
isDisabled={isDisabled}
operatorsList={operatorsList}
allowCustomOptions={allowCustomFieldOptions}
getExtendedFields={getExtendedFields}
/>
</EuiFlexItem>
</EuiFlexGroup>

View file

@ -42,11 +42,7 @@ export const getAllFieldsByName = (
keyBy('name', getAllBrowserFields(browserFields));
export const getIndexFields = memoizeOne(
(
title: string,
fields: IIndexPatternFieldList,
_includeUnmapped: boolean = false
): DataViewBase =>
(title: string, fields: IIndexPatternFieldList): DataViewBase =>
fields && fields.length > 0
? {
fields: fields.map((field) =>
@ -66,10 +62,7 @@ export const getIndexFields = memoizeOne(
title,
}
: { fields: [], title },
(newArgs, lastArgs) =>
newArgs[0] === lastArgs[0] &&
newArgs[1].length === lastArgs[1].length &&
newArgs[2] === lastArgs[2]
(newArgs, lastArgs) => newArgs[0] === lastArgs[0] && newArgs[1].length === lastArgs[1].length
);
const DEFAULT_BROWSER_FIELDS = {};
@ -96,8 +89,7 @@ interface FetchIndexReturn {
export const useFetchIndex = (
indexNames: string[],
onlyCheckIfIndicesExist: boolean = false,
strategy: 'indexFields' | 'dataView' | typeof ENDPOINT_FIELDS_SEARCH_STRATEGY = 'indexFields',
includeUnmapped: boolean = false
strategy: 'indexFields' | 'dataView' | typeof ENDPOINT_FIELDS_SEARCH_STRATEGY = 'indexFields'
): [boolean, FetchIndexReturn] => {
const { data } = useKibana().services;
const abortCtrl = useRef(new AbortController());
@ -121,11 +113,7 @@ export const useFetchIndex = (
abortCtrl.current = new AbortController();
const dv = await data.dataViews.create({ title: iNames.join(','), allowNoIndex: true });
const dataView = dv.toSpec();
const { browserFields } = getDataViewStateFromIndexFields(
iNames,
dataView.fields,
includeUnmapped
);
const { browserFields } = getDataViewStateFromIndexFields(iNames, dataView.fields);
previousIndexesName.current = dv.getIndexPattern().split(',');
@ -135,7 +123,7 @@ export const useFetchIndex = (
browserFields,
indexes: dv.getIndexPattern().split(','),
indexExists: dv.getIndexPattern().split(',').length > 0,
indexPatterns: getIndexFields(dv.getIndexPattern(), dv.fields, includeUnmapped),
indexPatterns: getIndexFields(dv.getIndexPattern(), dv.fields),
});
} catch (exc) {
setState({
@ -152,7 +140,7 @@ export const useFetchIndex = (
asyncSearch();
},
[addError, data.dataViews, includeUnmapped, indexNames, state]
[addError, data.dataViews, indexNames, state]
);
useEffect(() => {

View file

@ -48,11 +48,7 @@ interface DataViewInfo {
* VERY mutatious on purpose to improve the performance of the transform.
*/
export const getDataViewStateFromIndexFields = memoizeOne(
(
_title: string,
fields: DataViewSpec['fields'],
_includeUnmapped: boolean = false
): DataViewInfo => {
(_title: string, fields: DataViewSpec['fields']): DataViewInfo => {
// Adds two dangerous casts to allow for mutations within this function
type DangerCastForMutation = Record<string, {}>;
if (fields == null) {
@ -72,10 +68,7 @@ export const getDataViewStateFromIndexFields = memoizeOne(
return { browserFields: browserFields as DangerCastForBrowserFieldsMutation };
}
},
(newArgs, lastArgs) =>
newArgs[0] === lastArgs[0] &&
newArgs[1]?.length === lastArgs[1]?.length &&
newArgs[2] === lastArgs[2]
(newArgs, lastArgs) => newArgs[0] === lastArgs[0] && newArgs[1]?.length === lastArgs[1]?.length
);
export const useDataView = (): {

View file

@ -438,8 +438,7 @@ export const useSourcererDataView = (
const browserFields = useCallback(() => {
const { browserFields: dataViewBrowserFields } = getDataViewStateFromIndexFields(
sourcererDataView.patternList.join(','),
sourcererDataView.fields,
false
sourcererDataView.fields
);
return dataViewBrowserFields;
}, [sourcererDataView.fields, sourcererDataView.patternList]);

View file

@ -85,6 +85,7 @@ describe('When the add exception modal is opened', () => {
mockFetchIndexPatterns.mockImplementation(() => ({
isLoading: false,
indexPatterns: stubIndexPattern,
getExtendedFields: () => Promise.resolve([]),
}));
mockUseSignalIndex.mockImplementation(() => ({
@ -153,6 +154,7 @@ describe('When the add exception modal is opened', () => {
mockFetchIndexPatterns.mockImplementation(() => ({
isLoading: true,
indexPatterns: { fields: [], title: 'foo' },
getExtendedFields: () => Promise.resolve([]),
}));
wrapper = mount(

View file

@ -114,7 +114,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
onCancel,
onConfirm,
}: AddExceptionFlyoutProps) {
const { isLoading, indexPatterns } = useFetchIndexPatterns(rules);
const { isLoading, indexPatterns, getExtendedFields } = useFetchIndexPatterns(rules);
const [isSubmitting, submitNewExceptionItems] = useAddNewExceptionItems();
const [isClosingAlerts, closeAlerts] = useCloseAlertsFromExceptions();
const invalidateFetchRuleByIdQuery = useInvalidateFetchRuleByIdQuery();
@ -502,6 +502,7 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({
onExceptionItemAdd={setExceptionItemsToAdd}
onSetErrorExists={setConditionsValidationError}
onFilterIndexPatterns={filterIndexPatterns}
getExtendedFields={getExtendedFields}
/>
{listType !== ExceptionListTypeEnum.ENDPOINT && !sharedListToAddTo?.length && (

View file

@ -124,6 +124,7 @@ describe('When the edit exception modal is opened', () => {
mockFetchIndexPatterns.mockImplementation(() => ({
isLoading: false,
indexPatterns: stubIndexPattern,
getExtendedFields: () => Promise.resolve([]),
}));
mockUseFindExceptionListReferences.mockImplementation(() => [
false,
@ -168,6 +169,7 @@ describe('When the edit exception modal is opened', () => {
mockFetchIndexPatterns.mockImplementation(() => ({
isLoading: true,
indexPatterns: { fields: [], title: 'foo' },
getExtendedFields: () => Promise.resolve([]),
}));
const wrapper = mount(

View file

@ -109,7 +109,7 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({
const rules = useMemo(() => (rule != null ? [rule] : null), [rule]);
const listType = useMemo((): ExceptionListTypeEnum => list.type as ExceptionListTypeEnum, [list]);
const { isLoading, indexPatterns } = useFetchIndexPatterns(rules);
const { isLoading, indexPatterns, getExtendedFields } = useFetchIndexPatterns(rules);
const [isSubmitting, submitEditExceptionItems] = useEditExceptionItems();
const [isClosingAlerts, closeAlerts] = useCloseAlertsFromExceptions();
@ -370,6 +370,7 @@ const EditExceptionFlyoutComponent: React.FC<EditExceptionFlyoutProps> = ({
onExceptionItemAdd={setExceptionItemsToAdd}
onSetErrorExists={setConditionsValidationError}
onFilterIndexPatterns={filterIndexPatterns}
getExtendedFields={getExtendedFields}
/>
{!openedFromListDetailPage && listType === ExceptionListTypeEnum.DETECTION && (
<>

View file

@ -20,6 +20,7 @@ import type {
} from '@kbn/securitysolution-io-ts-list-types';
import { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
import type {
DataViewField,
ExceptionsBuilderExceptionItem,
ExceptionsBuilderReturnExceptionItem,
} from '@kbn/securitysolution-list-utils';
@ -89,6 +90,8 @@ interface ExceptionsFlyoutConditionsComponentProps {
type: ExceptionListType,
osTypes?: Array<'linux' | 'macos' | 'windows'> | undefined
) => DataViewBase;
getExtendedFields?: (fields: string[]) => Promise<DataViewField[]>;
}
const ExceptionsConditionsComponent: React.FC<ExceptionsFlyoutConditionsComponentProps> = ({
@ -105,6 +108,7 @@ const ExceptionsConditionsComponent: React.FC<ExceptionsFlyoutConditionsComponen
onExceptionItemAdd,
onSetErrorExists,
onFilterIndexPatterns,
getExtendedFields,
}): JSX.Element => {
const { http, unifiedSearch } = useKibana().services;
const isEndpointException = useMemo(
@ -267,6 +271,7 @@ const ExceptionsConditionsComponent: React.FC<ExceptionsFlyoutConditionsComponen
onChange: handleBuilderOnChange,
isDisabled: isExceptionBuilderFormDisabled,
allowCustomFieldOptions: !isEndpointException,
getExtendedFields,
})}
</>
);

View file

@ -5,9 +5,11 @@
* 2.0.
*/
import { useEffect, useState, useMemo } from 'react';
import { useEffect, useState, useMemo, useCallback } from 'react';
import type { DataViewBase } from '@kbn/es-query';
import type { FieldSpec, DataViewSpec } from '@kbn/data-views-plugin/common';
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
import type { Rule } from '../../rule_management/logic/types';
import { useGetInstalledJob } from '../../../common/components/ml/hooks/use_get_jobs';
@ -19,6 +21,7 @@ import * as i18n from '../../../common/containers/source/translations';
export interface ReturnUseFetchExceptionFlyoutData {
isLoading: boolean;
indexPatterns: DataViewBase;
getExtendedFields: (fields: string[]) => Promise<FieldSpec[]>;
}
/**
@ -84,15 +87,14 @@ export const useFetchIndexPatterns = (rules: Rule[] | null): ReturnUseFetchExcep
}
}, [jobs, isMLRule, memoDataViewId, memoNonDataViewIndexPatterns]);
const [isIndexPatternLoading, { indexPatterns: indexIndexPatterns }] = useFetchIndex(
memoRuleIndices,
false,
'indexFields',
true
);
const [
isIndexPatternLoading,
{ indexPatterns: indexIndexPatterns, dataView: indexDataViewSpec },
] = useFetchIndex(memoRuleIndices, false, 'indexFields');
// Data view logic
const [dataViewIndexPatterns, setDataViewIndexPatterns] = useState<DataViewBase | null>(null);
const [dataViewSpec, setDataViewSpec] = useState<DataViewSpec | null>(null);
useEffect(() => {
const fetchSingleDataView = async () => {
// ensure the memoized data view includes a space id, otherwise
@ -101,25 +103,36 @@ export const useFetchIndexPatterns = (rules: Rule[] | null): ReturnUseFetchExcep
if (activeSpaceId !== '' && memoDataViewId) {
setDataViewLoading(true);
const dv = await data.dataViews.get(memoDataViewId);
let fieldsWithUnmappedInfo = null;
try {
fieldsWithUnmappedInfo = await data.dataViews.getFieldsForIndexPattern(dv, {
pattern: '',
includeUnmapped: true,
});
} catch (error) {
addWarning(error, { title: i18n.FETCH_FIELDS_WITH_UNMAPPED_DATA_ERROR });
}
setDataViewLoading(false);
setDataViewIndexPatterns({
...dv,
...(fieldsWithUnmappedInfo ? { fields: fieldsWithUnmappedInfo } : {}),
});
setDataViewIndexPatterns(dv);
setDataViewSpec(dv.toSpec());
}
};
fetchSingleDataView();
}, [memoDataViewId, data.dataViews, setDataViewIndexPatterns, activeSpaceId, addWarning]);
}, [memoDataViewId, data.dataViews, setDataViewIndexPatterns, activeSpaceId]);
// Fetch extended fields information
const getExtendedFields = useCallback(
async (fields: string[]) => {
let extendedFields: FieldSpec[] = [];
const dv = dataViewSpec ?? indexDataViewSpec;
if (!dv) {
return extendedFields;
}
try {
extendedFields = await data.dataViews.getFieldsForIndexPattern(dv, {
pattern: '',
includeUnmapped: true,
fields,
});
} catch (error) {
addWarning(error, { title: i18n.FETCH_FIELDS_WITH_UNMAPPED_DATA_ERROR });
}
return extendedFields;
},
[addWarning, data.dataViews, dataViewSpec, indexDataViewSpec]
);
// Determine whether to use index patterns or data views
const indexPatternsToUse = useMemo(
@ -131,5 +144,6 @@ export const useFetchIndexPatterns = (rules: Rule[] | null): ReturnUseFetchExcep
return {
isLoading: isIndexPatternLoading || mlJobLoading || dataViewLoading,
indexPatterns: indexPatternsToUse,
getExtendedFields,
};
};