[Security Solutions] Removes deprecated types in kbn-securitysolution-* for newer kbn-es-query types (#106801)

## Summary

Fixes https://github.com/elastic/kibana/issues/105731, by replacing these `any` types:

```json
type IFieldType = any;
type IIndexPattern = any;
type Filter = any;
```

With the types from `es-query` which are:
* IndexPatternFieldBase
* IndexPatternBase
* Filter

Note: I had to do a few creative casting to avoid having to use `FieldSpec` since that is not within the package `es-query` and is not planned to be within that package or another package for at least a while if ever.

### Checklist

- [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
This commit is contained in:
Frank Hassanabad 2021-07-27 09:12:02 -06:00 committed by GitHub
parent fcaa4aaf40
commit 5dd68dd7b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 399 additions and 399 deletions

View file

@ -36,6 +36,7 @@ SRC_DEPS = [
"//packages/kbn-i18n",
"//packages/kbn-securitysolution-io-ts-list-types",
"//packages/kbn-securitysolution-list-hooks",
"//packages/kbn-es-query",
"@npm//@babel/core",
"@npm//babel-loader",
"@npm//@elastic/eui",

View file

@ -12,7 +12,7 @@ This hook uses the kibana `services.data.autocomplete.getValueSuggestions()` ser
This component can be used to display available indexPattern fields. It requires an indexPattern to be passed in and will show an error state if value is not one of the available indexPattern fields. Users will be able to select only one option.
The `onChange` handler is passed `IFieldType[]`.
The `onChange` handler is passed `IndexPatternFieldBase[]`.
```js
<FieldComponent

View file

@ -6,18 +6,15 @@
* Side Public License, v 1.
*/
import { IndexPatternFieldBase } from '@kbn/es-query';
import * as i18n from '../translations';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType } from '../../../../../../../src/plugins/data/common';
type IFieldType = any;
/**
* Determines if empty value is ok
*/
export const checkEmptyValue = (
param: string | undefined,
field: IFieldType | undefined,
field: IndexPatternFieldBase | undefined,
isRequired: boolean,
touched: boolean
): string | undefined | null => {

View file

@ -8,11 +8,7 @@
import React, { useCallback, useMemo, useState } from 'react';
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType, IIndexPattern } from '../../../../../../../../src/plugins/data/common';
type IFieldType = any;
type IIndexPattern = any;
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import {
getGenericComboBoxProps,
@ -24,14 +20,14 @@ const AS_PLAIN_TEXT = { asPlainText: true };
interface OperatorProps {
fieldInputWidth?: number;
fieldTypeFilter?: string[];
indexPattern: IIndexPattern | undefined;
indexPattern: IndexPatternBase | undefined;
isClearable: boolean;
isDisabled: boolean;
isLoading: boolean;
isRequired?: boolean;
onChange: (a: IFieldType[]) => void;
onChange: (a: IndexPatternFieldBase[]) => void;
placeholder: string;
selectedField: IFieldType | undefined;
selectedField: IndexPatternFieldBase | undefined;
}
export const FieldComponent: React.FC<OperatorProps> = ({
@ -60,7 +56,7 @@ export const FieldComponent: React.FC<OperatorProps> = ({
const handleValuesChange = useCallback(
(newOptions: EuiComboBoxOptionOption[]): void => {
const newValues: IFieldType[] = newOptions.map(
const newValues: IndexPatternFieldBase[] = newOptions.map(
({ label }) => availableFields[labels.indexOf(label)]
);
onChange(newValues);
@ -98,13 +94,13 @@ export const FieldComponent: React.FC<OperatorProps> = ({
FieldComponent.displayName = 'Field';
interface ComboBoxFields {
availableFields: IFieldType[];
selectedFields: IFieldType[];
availableFields: IndexPatternFieldBase[];
selectedFields: IndexPatternFieldBase[];
}
const getComboBoxFields = (
indexPattern: IIndexPattern | undefined,
selectedField: IFieldType | undefined,
indexPattern: IndexPatternBase | undefined,
selectedField: IndexPatternFieldBase | undefined,
fieldTypeFilter: string[]
): ComboBoxFields => {
const existingFields = getExistingFields(indexPattern);
@ -117,27 +113,29 @@ const getComboBoxFields = (
const getComboBoxProps = (fields: ComboBoxFields): GetGenericComboBoxPropsReturn => {
const { availableFields, selectedFields } = fields;
return getGenericComboBoxProps<IFieldType>({
return getGenericComboBoxProps<IndexPatternFieldBase>({
getLabel: (field) => field.name,
options: availableFields,
selectedOptions: selectedFields,
});
};
const getExistingFields = (indexPattern: IIndexPattern | undefined): IFieldType[] => {
const getExistingFields = (indexPattern: IndexPatternBase | undefined): IndexPatternFieldBase[] => {
return indexPattern != null ? indexPattern.fields : [];
};
const getSelectedFields = (selectedField: IFieldType | undefined): IFieldType[] => {
const getSelectedFields = (
selectedField: IndexPatternFieldBase | undefined
): IndexPatternFieldBase[] => {
return selectedField ? [selectedField] : [];
};
const getAvailableFields = (
existingFields: IFieldType[],
selectedFields: IFieldType[],
existingFields: IndexPatternFieldBase[],
selectedFields: IndexPatternFieldBase[],
fieldTypeFilter: string[]
): IFieldType[] => {
const fieldsByName = new Map<string, IFieldType>();
): IndexPatternFieldBase[] => {
const fieldsByName = new Map<string, IndexPatternFieldBase>();
existingFields.forEach((f) => fieldsByName.set(f.name, f));
selectedFields.forEach((f) => fieldsByName.set(f.name, f));

View file

@ -10,14 +10,11 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types';
import { useFindLists } from '@kbn/securitysolution-list-hooks';
import { IndexPatternFieldBase } from '@kbn/es-query';
import { filterFieldToList } from '../filter_field_to_list';
import { getGenericComboBoxProps } from '../get_generic_combo_box_props';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType } from '../../../../../../../src/plugins/data/common';
type IFieldType = any;
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715
// import { HttpStart } from 'kibana/public';
type HttpStart = any;
@ -34,7 +31,7 @@ interface AutocompleteFieldListsProps {
onChange: (arg: ListSchema) => void;
placeholder: string;
rowLabel?: string;
selectedField: IFieldType | undefined;
selectedField: IndexPatternFieldBase | undefined;
selectedValue: string | undefined;
}

View file

@ -54,7 +54,7 @@ describe('AutocompleteFieldMatchComponent', () => {
placeholder="Placeholder text"
rowLabel={'Row Label'}
selectedField={getField('ip')}
selectedValue="126.45.211.34"
selectedValue="127.0.0.1"
/>
);
@ -79,7 +79,7 @@ describe('AutocompleteFieldMatchComponent', () => {
onError={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue="126.45.211.34"
selectedValue="127.0.0.1"
/>
);
@ -104,7 +104,7 @@ describe('AutocompleteFieldMatchComponent', () => {
onError={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue="126.45.211.34"
selectedValue="127.0.0.1"
/>
);
wrapper.find('[data-test-subj="valuesAutocompleteMatch"] button').at(0).simulate('click');
@ -131,7 +131,7 @@ describe('AutocompleteFieldMatchComponent', () => {
onError={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue="126.45.211.34"
selectedValue="127.0.0.1"
/>
);
@ -158,13 +158,13 @@ describe('AutocompleteFieldMatchComponent', () => {
onError={jest.fn()}
placeholder="Placeholder text"
selectedField={getField('ip')}
selectedValue="126.45.211.34"
selectedValue="127.0.0.1"
/>
);
expect(
wrapper.find('[data-test-subj="valuesAutocompleteMatch"] EuiComboBoxPill').at(0).text()
).toEqual('126.45.211.34');
).toEqual('127.0.0.1');
});
test('it invokes "onChange" when new value created', async () => {
@ -190,9 +190,9 @@ describe('AutocompleteFieldMatchComponent', () => {
((wrapper.find(EuiComboBox).props() as unknown) as {
onCreateOption: (a: string) => void;
}).onCreateOption('126.45.211.34');
}).onCreateOption('127.0.0.1');
expect(mockOnChange).toHaveBeenCalledWith('126.45.211.34');
expect(mockOnChange).toHaveBeenCalledWith('127.0.0.1');
});
test('it invokes "onChange" when new value selected', async () => {

View file

@ -14,6 +14,8 @@ import {
EuiComboBoxOptionOption,
EuiComboBox,
} from '@elastic/eui';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import { uniq } from 'lodash';
import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
@ -22,11 +24,6 @@ import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-
// import { AutocompleteStart } from '../../../../../../../src/plugins/data/public';
type AutocompleteStart = any;
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType, IIndexPattern } from '../../../../../../../../src/plugins/data/common';
type IFieldType = any;
type IIndexPattern = any;
import * as i18n from '../translations';
import { useFieldValueAutocomplete } from '../hooks/use_field_value_autocomplete';
import {
@ -44,9 +41,9 @@ const SINGLE_SELECTION = { asPlainText: true };
interface AutocompleteFieldMatchProps {
placeholder: string;
selectedField: IFieldType | undefined;
selectedField: IndexPatternFieldBase | undefined;
selectedValue: string | undefined;
indexPattern: IIndexPattern | undefined;
indexPattern: IndexPatternBase | undefined;
isLoading: boolean;
isDisabled: boolean;
isClearable: boolean;

View file

@ -64,7 +64,7 @@ describe('AutocompleteFieldMatchAnyComponent', () => {
placeholder="Placeholder text"
rowLabel={'Row Label'}
selectedField={getField('ip')}
selectedValue={['126.45.211.34']}
selectedValue={['127.0.0.1']}
/>
);
@ -124,7 +124,7 @@ describe('AutocompleteFieldMatchAnyComponent', () => {
placeholder="Placeholder text"
rowLabel={'Row Label'}
selectedField={getField('ip')}
selectedValue={['126.45.211.34']}
selectedValue={['127.0.0.1']}
/>
);
@ -155,13 +155,13 @@ describe('AutocompleteFieldMatchAnyComponent', () => {
placeholder="Placeholder text"
rowLabel={'Row Label'}
selectedField={getField('ip')}
selectedValue={['126.45.211.34']}
selectedValue={['127.0.0.1']}
/>
);
expect(
wrapper.find(`[data-test-subj="valuesAutocompleteMatchAny"] EuiComboBoxPill`).at(0).text()
).toEqual('126.45.211.34');
).toEqual('127.0.0.1');
});
test('it invokes "onChange" when new value created', async () => {
@ -191,9 +191,9 @@ describe('AutocompleteFieldMatchAnyComponent', () => {
((wrapper.find(EuiComboBox).props() as unknown) as {
onCreateOption: (a: string) => void;
}).onCreateOption('126.45.211.34');
}).onCreateOption('127.0.0.1');
expect(mockOnChange).toHaveBeenCalledWith(['126.45.211.34']);
expect(mockOnChange).toHaveBeenCalledWith(['127.0.0.1']);
});
test('it invokes "onChange" when new value selected', async () => {

View file

@ -10,16 +10,12 @@ import React, { useCallback, useMemo, useState } from 'react';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui';
import { uniq } from 'lodash';
import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715
// import { AutocompleteStart } from '../../../../../../../src/plugins/data/public';
type AutocompleteStart = any;
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType, IIndexPattern } from '../../../../../../../../src/plugins/data/common';
type IFieldType = any;
type IIndexPattern = any;
import * as i18n from '../translations';
import {
getGenericComboBoxProps,
@ -30,9 +26,9 @@ import { paramIsValid } from '../param_is_valid';
interface AutocompleteFieldMatchAnyProps {
placeholder: string;
selectedField: IFieldType | undefined;
selectedField: IndexPatternFieldBase | undefined;
selectedValue: string[];
indexPattern: IIndexPattern | undefined;
indexPattern: IndexPatternBase | undefined;
isLoading: boolean;
isDisabled: boolean;
isClearable: boolean;

View file

@ -6,11 +6,12 @@
* Side Public License, v 1.
*/
// Copied from "src/plugins/data/common/index_patterns/fields/fields.mocks.ts"
// but without types.
import { IndexPatternFieldBase } from '@kbn/es-query';
// Copied from "src/plugins/data/common/index_patterns/fields/fields.mocks.ts" but with the types changed to "IndexPatternFieldBase" since that type is compatible.
// TODO: This should move out once those mocks are directly useable or in their own package, https://github.com/elastic/kibana/issues/100715
export const fields = [
export const fields: IndexPatternFieldBase[] = ([
{
name: 'bytes',
type: 'number',
@ -308,6 +309,6 @@ export const fields = [
readFromDocValues: false,
subType: { nested: { path: 'nestedField.nestedChild' } },
},
];
] as unknown) as IndexPatternFieldBase[];
export const getField = (name: string) => fields.find((field) => field.name === name);

View file

@ -10,10 +10,7 @@ import { filterFieldToList } from '.';
import type { ListSchema } from '@kbn/securitysolution-io-ts-list-types';
import { getListResponseMock } from '../list_schema/index.mock';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType } from '../../../../../../../src/plugins/data/common';
type IFieldType = any;
import { IndexPatternFieldBase } from '@kbn/es-query';
describe('#filterFieldToList', () => {
test('it returns empty array if given a undefined for field', () => {
@ -22,13 +19,20 @@ describe('#filterFieldToList', () => {
});
test('it returns empty array if filed does not contain esTypes', () => {
const field: IFieldType = { name: 'some-name', type: 'some-type' };
const field: IndexPatternFieldBase = {
name: 'some-name',
type: 'some-type',
};
const filter = filterFieldToList([], field);
expect(filter).toEqual([]);
});
test('it returns single filtered list of ip_range -> ip', () => {
const field: IFieldType = { esTypes: ['ip'], name: 'some-name', type: 'ip' };
const field: IndexPatternFieldBase & { esTypes: string[] } = {
esTypes: ['ip'],
name: 'some-name',
type: 'ip',
};
const listItem: ListSchema = { ...getListResponseMock(), type: 'ip_range' };
const filter = filterFieldToList([listItem], field);
const expected: ListSchema[] = [listItem];
@ -36,7 +40,11 @@ describe('#filterFieldToList', () => {
});
test('it returns single filtered list of ip -> ip', () => {
const field: IFieldType = { esTypes: ['ip'], name: 'some-name', type: 'ip' };
const field: IndexPatternFieldBase & { esTypes: string[] } = {
esTypes: ['ip'],
name: 'some-name',
type: 'ip',
};
const listItem: ListSchema = { ...getListResponseMock(), type: 'ip' };
const filter = filterFieldToList([listItem], field);
const expected: ListSchema[] = [listItem];
@ -44,7 +52,11 @@ describe('#filterFieldToList', () => {
});
test('it returns single filtered list of keyword -> keyword', () => {
const field: IFieldType = { esTypes: ['keyword'], name: 'some-name', type: 'keyword' };
const field: IndexPatternFieldBase & { esTypes: string[] } = {
esTypes: ['keyword'],
name: 'some-name',
type: 'keyword',
};
const listItem: ListSchema = { ...getListResponseMock(), type: 'keyword' };
const filter = filterFieldToList([listItem], field);
const expected: ListSchema[] = [listItem];
@ -52,7 +64,11 @@ describe('#filterFieldToList', () => {
});
test('it returns single filtered list of text -> text', () => {
const field: IFieldType = { esTypes: ['text'], name: 'some-name', type: 'text' };
const field: IndexPatternFieldBase & { esTypes: string[] } = {
esTypes: ['text'],
name: 'some-name',
type: 'text',
};
const listItem: ListSchema = { ...getListResponseMock(), type: 'text' };
const filter = filterFieldToList([listItem], field);
const expected: ListSchema[] = [listItem];
@ -60,7 +76,11 @@ describe('#filterFieldToList', () => {
});
test('it returns 2 filtered lists of ip_range -> ip', () => {
const field: IFieldType = { esTypes: ['ip'], name: 'some-name', type: 'ip' };
const field: IndexPatternFieldBase & { esTypes: string[] } = {
esTypes: ['ip'],
name: 'some-name',
type: 'ip',
};
const listItem1: ListSchema = { ...getListResponseMock(), type: 'ip_range' };
const listItem2: ListSchema = { ...getListResponseMock(), type: 'ip_range' };
const filter = filterFieldToList([listItem1, listItem2], field);
@ -69,7 +89,11 @@ describe('#filterFieldToList', () => {
});
test('it returns 1 filtered lists of ip_range -> ip if the 2nd is not compatible type', () => {
const field: IFieldType = { esTypes: ['ip'], name: 'some-name', type: 'ip' };
const field: IndexPatternFieldBase & { esTypes: string[] } = {
esTypes: ['ip'],
name: 'some-name',
type: 'ip',
};
const listItem1: ListSchema = { ...getListResponseMock(), type: 'ip_range' };
const listItem2: ListSchema = { ...getListResponseMock(), type: 'text' };
const filter = filterFieldToList([listItem1, listItem2], field);

View file

@ -7,19 +7,23 @@
*/
import { ListSchema } from '@kbn/securitysolution-io-ts-list-types';
import { IndexPatternFieldBase } from '@kbn/es-query';
import { typeMatch } from '../type_match';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType, IIndexPattern } from '../../../../../../../../src/plugins/data/common';
type IFieldType = any;
/**
* Given an array of lists and optionally a field this will return all
* the lists that match against the field based on the types from the field
*
* NOTE: That we support one additional property from "FieldSpec" located here:
* src/plugins/data/common/index_patterns/fields/types.ts
* This type property is esTypes. If it exists and is on there we will read off the esTypes.
* @param lists The lists to match against the field
* @param field The field to check against the list to see if they are compatible
*/
export const filterFieldToList = (lists: ListSchema[], field?: IFieldType): ListSchema[] => {
export const filterFieldToList = (
lists: ListSchema[],
field?: IndexPatternFieldBase & { esTypes?: string[] }
): ListSchema[] => {
if (field != null) {
const { esTypes = [] } = field;
return lists.filter(({ type }) => esTypes.some((esType: string) => typeMatch(type, esType)));

View file

@ -31,13 +31,8 @@ describe('#getOperators', () => {
test('it returns "isOperator" when field type is "nested"', () => {
const operator = getOperators({
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'nestedField',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: { nested: { path: 'nestedField' } },
type: 'nested',
});

View file

@ -6,9 +6,7 @@
* Side Public License, v 1.
*/
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType } from '../../../../../../../src/plugins/data/common';
type IFieldType = any;
import { IndexPatternFieldBase } from '@kbn/es-query';
import {
EXCEPTION_OPERATORS,
@ -22,10 +20,10 @@ import {
/**
* Returns the appropriate operators given a field type
*
* @param field IFieldType selected field
* @param field IndexPatternFieldBase selected field
*
*/
export const getOperators = (field: IFieldType | undefined): OperatorOption[] => {
export const getOperators = (field: IndexPatternFieldBase | undefined): OperatorOption[] => {
if (field == null) {
return [isOperator];
} else if (field.type === 'boolean') {

View file

@ -16,6 +16,7 @@ import {
} from '.';
import { getField } from '../../fields/index.mock';
import { autocompleteStartMock } from '../../autocomplete/index.mock';
import { IndexPatternFieldBase } from '@kbn/es-query';
// Copied from "src/plugins/data/common/index_patterns/index_pattern.stub.ts"
// TODO: Remove this in favor of the above if/when it is ported, https://github.com/elastic/kibana/issues/100715
@ -152,6 +153,11 @@ describe('use_field_value_autocomplete', () => {
const suggestionsMock = jest.fn().mockResolvedValue([]);
await act(async () => {
const selectedField: IndexPatternFieldBase | undefined = getField('nestedField.child');
if (selectedField == null) {
throw new TypeError('selectedField for this test should always be defined');
}
const { signal } = new AbortController();
const { waitForNextUpdate } = renderHook<
UseFieldValueAutocompleteProps,
@ -166,7 +172,7 @@ describe('use_field_value_autocomplete', () => {
indexPattern: stubIndexPatternWithFields,
operatorType: OperatorTypeEnum.MATCH,
query: '',
selectedField: { ...getField('nestedField.child'), name: 'child' },
selectedField: { ...selectedField, name: 'child' },
})
);
// Note: initial `waitForNextUpdate` is hook initialization

View file

@ -9,19 +9,15 @@
import { useEffect, useRef, useState } from 'react';
import { debounce } from 'lodash';
import { ListOperatorTypeEnum as OperatorTypeEnum } from '@kbn/securitysolution-io-ts-list-types';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/100715
// import { AutocompleteStart } from '../../../../../../../../src/plugins/data/public';
type AutocompleteStart = any;
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType, IIndexPattern } from '../../../../../../../../src/plugins/data/common';
type IFieldType = any;
type IIndexPattern = any;
interface FuncArgs {
fieldSelected: IFieldType | undefined;
patterns: IIndexPattern | undefined;
fieldSelected: IndexPatternFieldBase | undefined;
patterns: IndexPatternBase | undefined;
searchQuery: string;
value: string | string[] | undefined;
}
@ -33,10 +29,10 @@ export type UseFieldValueAutocompleteReturn = [boolean, boolean, string[], Func
export interface UseFieldValueAutocompleteProps {
autocompleteService: AutocompleteStart;
fieldValue: string | string[] | undefined;
indexPattern: IIndexPattern | undefined;
indexPattern: IndexPatternBase | undefined;
operatorType: OperatorTypeEnum;
query: string;
selectedField: IFieldType | undefined;
selectedField: IndexPatternFieldBase | undefined;
}
/**
* Hook for using the field value autocomplete service

View file

@ -160,13 +160,8 @@ describe('operator', () => {
operator={isOperator}
placeholder="Placeholder text"
selectedField={{
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'nestedField',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: { nested: { path: 'nestedField' } },
type: 'nested',
}}

View file

@ -9,10 +9,7 @@
import React, { useCallback, useMemo } from 'react';
import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import { OperatorOption } from '@kbn/securitysolution-list-utils';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType } from '../../../../../../../src/plugins/data/common';
type IFieldType = any;
import { IndexPatternFieldBase } from '@kbn/es-query';
import { getOperators } from '../get_operators';
import {
@ -31,7 +28,7 @@ interface OperatorState {
operatorInputWidth?: number;
operatorOptions?: OperatorOption[];
placeholder: string;
selectedField: IFieldType | undefined;
selectedField: IndexPatternFieldBase | undefined;
}
export const OperatorComponent: React.FC<OperatorState> = ({

View file

@ -7,12 +7,9 @@
*/
import dateMath from '@elastic/datemath';
import { IndexPatternFieldBase } from '@kbn/es-query';
import { checkEmptyValue } from '../check_empty_value';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types, https://github.com/elastic/kibana/issues/105731
// import { IFieldType } from '../../../../../../../src/plugins/data/common';
type IFieldType = any;
import * as i18n from '../translations';
/**
@ -25,7 +22,7 @@ import * as i18n from '../translations';
*/
export const paramIsValid = (
param: string | undefined,
field: IFieldType | undefined,
field: IndexPatternFieldBase | undefined,
isRequired: boolean,
touched: boolean
): string | undefined => {

View file

@ -32,6 +32,7 @@ SRC_DEPS = [
"//packages/kbn-securitysolution-list-constants",
"//packages/kbn-securitysolution-io-ts-list-types",
"//packages/kbn-securitysolution-utils",
"//packages/kbn-es-query",
"@npm//lodash",
"@npm//tslib",
]

View file

@ -21,17 +21,10 @@ import {
entriesNested,
OsTypeArray,
} from '@kbn/securitysolution-io-ts-list-types';
import { Filter } from '@kbn/es-query';
import { hasLargeValueList } from '../has_large_value_list';
/**
* Originally this was an import type of:
* import type { Filter } from '../../../../../src/plugins/data/common';
* TODO: Once we have the type for this within kbn packages, replace this with that one
* @deprecated
*/
type Filter = any;
type NonListEntry = EntryMatch | EntryMatchAny | EntryNested | EntryExists;
interface ExceptionListItemNonLargeList extends ExceptionListItemSchema {
entries: NonListEntry[];
@ -190,7 +183,7 @@ export const buildExceptionFilter = ({
} else {
const chunks = chunkExceptions(exceptionsWithoutLargeValueLists, chunkSize);
const filters = chunks.map<Filter>((exceptionsChunk) => {
const filters = chunks.map((exceptionsChunk) => {
const orClauses = createOrClauses(exceptionsChunk);
return {

View file

@ -28,11 +28,7 @@ import {
exceptionListItemSchema,
nestedEntryItem,
} from '@kbn/securitysolution-io-ts-list-types';
// TODO: I have to use any here for now, but once this is available below, we should use the correct types
// import { IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data/public';
type IFieldType = any;
type IIndexPattern = any;
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import {
EXCEPTION_OPERATORS,
@ -282,17 +278,17 @@ export const getUpdatedEntriesOnDelete = (
* add nested entry, should only show nested fields, if item is the parent
* field of a nested entry, we only display the parent field
*
* @param patterns IIndexPattern containing available fields on rule index
* @param patterns IndexPatternBase containing available fields on rule index
* @param item exception item entry
* set to add a nested field
*/
export const getFilteredIndexPatterns = (
patterns: IIndexPattern,
patterns: IndexPatternBase,
item: FormattedBuilderEntry,
type: ExceptionListType,
preFilter?: (i: IIndexPattern, t: ExceptionListType, o?: OsTypeArray) => IIndexPattern,
preFilter?: (i: IndexPatternBase, t: ExceptionListType, o?: OsTypeArray) => IndexPatternBase,
osTypes?: OsTypeArray
): IIndexPattern => {
): IndexPatternBase => {
const indexPatterns = preFilter != null ? preFilter(patterns, type, osTypes) : patterns;
if (item.nested === 'child' && item.parent != null) {
@ -300,7 +296,6 @@ export const getFilteredIndexPatterns = (
return {
...indexPatterns,
fields: indexPatterns.fields
// @ts-expect-error This will go away once we type IField from any
.filter((indexField) => {
const fieldHasCommonParentPath =
indexField.subType != null &&
@ -310,7 +305,6 @@ export const getFilteredIndexPatterns = (
return fieldHasCommonParentPath;
})
// @ts-expect-error This will go away once we type IField from any
.map((f) => {
const [fieldNameWithoutParentPath] = f.name.split('.').slice(-1);
return { ...f, name: fieldNameWithoutParentPath };
@ -324,7 +318,6 @@ export const getFilteredIndexPatterns = (
return {
...indexPatterns,
fields: indexPatterns.fields.filter(
// @ts-expect-error This will go away once we type IField from any
(field) => field.subType != null && field.subType.nested != null
),
};
@ -342,7 +335,7 @@ export const getFilteredIndexPatterns = (
*/
export const getEntryOnFieldChange = (
item: FormattedBuilderEntry,
newField: IFieldType
newField: IndexPatternFieldBase
): { index: number; updatedEntry: BuilderEntry } => {
const { parent, entryIndex, nested } = item;
const newChildFieldValue = newField != null ? newField.name.split('.').slice(-1)[0] : '';
@ -657,9 +650,9 @@ export const getCorrespondingKeywordField = ({
fields,
selectedField,
}: {
fields: IFieldType[];
fields: IndexPatternFieldBase[];
selectedField: string | undefined;
}): IFieldType | undefined => {
}): IndexPatternFieldBase | undefined => {
const selectedFieldBits =
selectedField != null && selectedField !== '' ? selectedField.split('.') : [];
const selectedFieldIsTextType = selectedFieldBits.slice(-1)[0] === 'text';
@ -679,7 +672,7 @@ export const getCorrespondingKeywordField = ({
* Formats the entry into one that is easily usable for the UI, most of the
* complexity was introduced with nested fields
*
* @param patterns IIndexPattern containing available fields on rule index
* @param patterns IndexPatternBase containing available fields on rule index
* @param item exception item entry
* @param itemIndex entry index
* @param parent nested entries hold copy of their parent for use in various logic
@ -687,7 +680,7 @@ export const getCorrespondingKeywordField = ({
* was added to ensure that nested items could be identified with their parent entry
*/
export const getFormattedBuilderEntry = (
indexPattern: IIndexPattern,
indexPattern: IndexPatternBase,
item: BuilderEntry,
itemIndex: number,
parent: EntryNested | undefined,
@ -695,7 +688,6 @@ export const getFormattedBuilderEntry = (
): FormattedBuilderEntry => {
const { fields } = indexPattern;
const field = parent != null ? `${parent.field}.${item.field}` : item.field;
// @ts-expect-error This will go away once we type IField from any
const [foundField] = fields.filter(({ name }) => field != null && field === name);
const correspondingKeywordField = getCorrespondingKeywordField({
fields,
@ -734,7 +726,7 @@ export const getFormattedBuilderEntry = (
* Formats the entries to be easily usable for the UI, most of the
* complexity was introduced with nested fields
*
* @param patterns IIndexPattern containing available fields on rule index
* @param patterns IndexPatternBase containing available fields on rule index
* @param entries exception item entries
* @param addNested boolean noting whether or not UI is currently
* set to add a nested field
@ -743,7 +735,7 @@ export const getFormattedBuilderEntry = (
* was added to ensure that nested items could be identified with their parent entry
*/
export const getFormattedBuilderEntries = (
indexPattern: IIndexPattern,
indexPattern: IndexPatternBase,
entries: BuilderEntry[],
parent?: EntryNested,
parentIndex?: number
@ -765,13 +757,14 @@ export const getFormattedBuilderEntries = (
entryIndex: index,
field: isNewNestedEntry
? undefined
: {
: // This type below is really a FieldSpec type from "src/plugins/data/common/index_patterns/fields/types.ts", we cast it here to keep using the IndexPatternFieldBase interface
({
aggregatable: false,
esTypes: ['nested'],
name: item.field != null ? item.field : '',
searchable: false,
type: 'string',
},
} as IndexPatternFieldBase),
id: item.id != null ? item.id : `${index}`,
nested: 'parent',
operator: isOperator,

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import { IndexPatternFieldBase } from '@kbn/es-query';
import type {
CreateExceptionListItemSchema,
Entry,
@ -30,21 +31,15 @@ export interface OperatorOption {
type: OperatorTypeEnum;
}
/**
* @deprecated Use the one from core once it is in its own package which will be from:
* Original import was // import { IFieldType } from '../../../../../../../src/plugins/data/common';
*/
type IFieldType = any;
export interface FormattedBuilderEntry {
id: string;
field: IFieldType | undefined;
field: IndexPatternFieldBase | undefined;
operator: OperatorOption;
value: string | string[] | undefined;
nested: 'parent' | 'child' | undefined;
entryIndex: number;
parent: { parent: BuilderEntryNested; parentIndex: number } | undefined;
correspondingKeywordField: IFieldType | undefined;
correspondingKeywordField: IndexPatternFieldBase | undefined;
}
export interface EmptyEntry {

View file

@ -119,7 +119,7 @@ export default {
},
indexPatterns: {
description:
'`IIndexPattern` - index patterns used to populate field options and value autocomplete.',
'`IndexPatternBase` - index patterns used to populate field options and value autocomplete.',
type: {
required: true,
},
@ -201,7 +201,7 @@ export default {
},
listTypeSpecificIndexPatternFilter: {
description:
'`(pattern: IIndexPattern, type: ExceptionListType) => IIndexPattern` - callback invoked when index patterns filtered. Optional to be used if you would only like certain fields displayed.',
'`(pattern: IndexPatternBase, type: ExceptionListType) => IndexPatternBase` - callback invoked when index patterns filtered. Optional to be used if you would only like certain fields displayed.',
type: {
required: false,
},

View file

@ -102,7 +102,7 @@ export default {
},
indexPattern: {
description:
'`IIndexPattern` - index patterns used to populate field options and value autocomplete.',
'`IndexPatternBase` - index patterns used to populate field options and value autocomplete.',
type: {
required: true,
},

View file

@ -20,6 +20,7 @@ import {
isOperator,
} from '@kbn/securitysolution-list-utils';
import { useFindLists } from '@kbn/securitysolution-list-hooks';
import { FieldSpec } from 'src/plugins/data/common';
import {
fields,
@ -374,31 +375,33 @@ describe('BuilderEntryItem', () => {
});
test('it uses "correspondingKeywordField" if it exists', () => {
const correspondingKeywordField: FieldSpec = {
aggregatable: true,
count: 0,
esTypes: ['keyword'],
name: 'extension',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'string',
};
const field: FieldSpec = {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'extension.text',
readFromDocValues: true,
scripted: false,
searchable: false,
type: 'string',
};
wrapper = mount(
<BuilderEntryItem
autocompleteService={autocompleteStartMock}
entry={{
correspondingKeywordField: {
aggregatable: true,
count: 0,
esTypes: ['keyword'],
name: 'extension',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'string',
},
correspondingKeywordField,
entryIndex: 0,
field: {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'extension.text',
readFromDocValues: true,
scripted: false,
searchable: false,
type: 'string',
},
field,
id: '123',
nested: undefined,
operator: isOneOfOperator,
@ -538,10 +541,10 @@ describe('BuilderEntryItem', () => {
((wrapper.find(EuiComboBox).at(2).props() as unknown) as {
onCreateOption: (a: string) => void;
}).onCreateOption('126.45.211.34');
}).onCreateOption('127.0.0.1');
expect(mockOnChange).toHaveBeenCalledWith(
{ field: 'ip', id: '123', operator: 'excluded', type: 'match', value: '126.45.211.34' },
{ field: 'ip', id: '123', operator: 'excluded', type: 'match', value: '127.0.0.1' },
0
);
});
@ -576,10 +579,10 @@ describe('BuilderEntryItem', () => {
((wrapper.find(EuiComboBox).at(2).props() as unknown) as {
onCreateOption: (a: string) => void;
}).onCreateOption('126.45.211.34');
}).onCreateOption('127.0.0.1');
expect(mockOnChange).toHaveBeenCalledWith(
{ field: 'ip', id: '123', operator: 'included', type: 'match_any', value: ['126.45.211.34'] },
{ field: 'ip', id: '123', operator: 'included', type: 'match_any', value: ['127.0.0.1'] },
0
);
});

View file

@ -35,9 +35,9 @@ import {
FieldComponent,
OperatorComponent,
} from '@kbn/securitysolution-autocomplete';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import { AutocompleteStart } from '../../../../../../../src/plugins/data/public';
import { IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data/common';
import { HttpStart } from '../../../../../../../src/core/public';
import { getEmptyValue } from '../../../common/empty_value';
@ -52,15 +52,15 @@ export interface EntryItemProps {
autocompleteService: AutocompleteStart;
entry: FormattedBuilderEntry;
httpService: HttpStart;
indexPattern: IIndexPattern;
indexPattern: IndexPatternBase;
showLabel: boolean;
osTypes?: OsTypeArray;
listType: ExceptionListType;
listTypeSpecificIndexPatternFilter?: (
pattern: IIndexPattern,
pattern: IndexPatternBase,
type: ExceptionListType,
osTypes?: OsTypeArray
) => IIndexPattern;
) => IndexPatternBase;
onChange: (arg: BuilderEntry, i: number) => void;
onlyShowListOperators?: boolean;
setErrorsExist: (arg: boolean) => void;
@ -90,7 +90,7 @@ export const BuilderEntryItem: React.FC<EntryItemProps> = ({
);
const handleFieldChange = useCallback(
([newField]: IFieldType[]): void => {
([newField]: IndexPatternFieldBase[]): void => {
const { updatedEntry, index } = getEntryOnFieldChange(entry, newField);
onChange(updatedEntry, index);
},

View file

@ -18,8 +18,7 @@ import {
getFormattedBuilderEntries,
getUpdatedEntriesOnDelete,
} from '@kbn/securitysolution-list-utils';
import { IIndexPattern } from '../../../../../../../src/plugins/data/common';
import { IndexPatternBase } from '@kbn/es-query';
import { BuilderAndBadgeComponent } from './and_badge';
import { BuilderEntryDeleteButtonComponent } from './entry_delete_button';
@ -47,15 +46,15 @@ interface BuilderExceptionListItemProps {
exceptionItem: ExceptionsBuilderExceptionItem;
exceptionItemIndex: number;
osTypes?: OsTypeArray;
indexPattern: IIndexPattern;
indexPattern: IndexPatternBase;
andLogicIncluded: boolean;
isOnlyItem: boolean;
listType: ExceptionListType;
listTypeSpecificIndexPatternFilter?: (
pattern: IIndexPattern,
pattern: IndexPatternBase,
type: ExceptionListType,
osTypes?: OsTypeArray
) => IIndexPattern;
) => IndexPatternBase;
onDeleteExceptionItem: (item: ExceptionsBuilderExceptionItem, index: number) => void;
onChangeExceptionItem: (item: ExceptionsBuilderExceptionItem, index: number) => void;
setErrorsExist: (arg: boolean) => void;

View file

@ -30,8 +30,9 @@ import {
getDefaultNestedEmptyEntry,
getNewExceptionItem,
} from '@kbn/securitysolution-list-utils';
import { IndexPatternBase } from '@kbn/es-query';
import { AutocompleteStart, IIndexPattern } from '../../../../../../../src/plugins/data/public';
import { AutocompleteStart } from '../../../../../../../src/plugins/data/public';
import { AndOrBadge } from '../and_or_badge';
import { BuilderExceptionListItemComponent } from './exception_item_renderer';
@ -75,7 +76,7 @@ export interface ExceptionBuilderProps {
exceptionListItems: ExceptionsBuilderExceptionItem[];
httpService: HttpStart;
osTypes?: OsTypeArray;
indexPatterns: IIndexPattern;
indexPatterns: IndexPatternBase;
isAndDisabled: boolean;
isNestedDisabled: boolean;
isOrDisabled: boolean;
@ -83,9 +84,9 @@ export interface ExceptionBuilderProps {
listNamespaceType: NamespaceType;
listType: ExceptionListType;
listTypeSpecificIndexPatternFilter?: (
pattern: IIndexPattern,
pattern: IndexPatternBase,
type: ExceptionListType
) => IIndexPattern;
) => IndexPatternBase;
onChange: (arg: OnChangeProps) => void;
ruleName: string;
isDisabled?: boolean;

View file

@ -52,6 +52,7 @@ import {
isOneOfOperator,
isOperator,
} from '@kbn/securitysolution-list-utils';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import { ENTRIES_WITH_IDS } from '../../../../common/constants.mock';
import { getEntryExistsMock } from '../../../../common/schemas/types/entry_exists.mock';
@ -60,7 +61,7 @@ import {
fields,
getField,
} from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks';
import { IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data/common';
import { FieldSpec } from '../../../../../../../src/plugins/data/common';
import { getEntryNestedMock } from '../../../../common/schemas/types/entry_nested.mock';
import { getEntryMatchMock } from '../../../../common/schemas/types/entry_match.mock';
import { getEntryMatchAnyMock } from '../../../../common/schemas/types/entry_match_any.mock';
@ -93,7 +94,7 @@ const getEntryMatchAnyWithIdMock = (): EntryMatchAny & { id: string } => ({
id: '123',
});
const getMockIndexPattern = (): IIndexPattern => ({
const getMockIndexPattern = (): IndexPatternBase => ({
fields,
id: '1234',
title: 'logstash-*',
@ -131,7 +132,11 @@ const getMockNestedBuilderEntry = (): FormattedBuilderEntry => ({
const getMockNestedParentBuilderEntry = (): FormattedBuilderEntry => ({
correspondingKeywordField: undefined,
entryIndex: 0,
field: { ...getField('nestedField.child'), esTypes: ['nested'], name: 'nestedField' },
field: {
...getField('nestedField.child'),
esTypes: ['nested'],
name: 'nestedField',
} as FieldSpec,
id: '123',
nested: 'parent',
operator: isOperator,
@ -163,10 +168,13 @@ const mockEndpointFields = [
},
];
export const getEndpointField = (name: string): IFieldType =>
mockEndpointFields.find((field) => field.name === name) as IFieldType;
export const getEndpointField = (name: string): IndexPatternFieldBase =>
mockEndpointFields.find((field) => field.name === name) as IndexPatternFieldBase;
const filterIndexPatterns = (patterns: IIndexPattern, type: ExceptionListType): IIndexPattern => {
const filterIndexPatterns = (
patterns: IndexPatternBase,
type: ExceptionListType
): IndexPatternBase => {
return type === 'endpoint'
? {
...patterns,
@ -181,10 +189,10 @@ describe('Exception builder helpers', () => {
describe('#getFilteredIndexPatterns', () => {
describe('list type detections', () => {
test('it returns nested fields that match parent value when "item.nested" is "child"', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItem: FormattedBuilderEntry = getMockNestedBuilderEntry();
const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'detection');
const expected: IIndexPattern = {
const expected: IndexPatternBase = {
fields: [{ ...getField('nestedField.child'), name: 'child' }],
id: '1234',
title: 'logstash-*',
@ -193,10 +201,10 @@ describe('Exception builder helpers', () => {
});
test('it returns only parent nested field when "item.nested" is "parent" and nested parent field is not undefined', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItem: FormattedBuilderEntry = getMockNestedParentBuilderEntry();
const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'detection');
const expected: IIndexPattern = {
const expected: IndexPatternBase & { fields: Array<Partial<FieldSpec>> } = {
fields: [{ ...getField('nestedField.child'), esTypes: ['nested'], name: 'nestedField' }],
id: '1234',
title: 'logstash-*',
@ -205,13 +213,13 @@ describe('Exception builder helpers', () => {
});
test('it returns only nested fields when "item.nested" is "parent" and nested parent field is undefined', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItem: FormattedBuilderEntry = {
...getMockNestedParentBuilderEntry(),
field: undefined,
};
const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'detection');
const expected: IIndexPattern = {
const expected: IndexPatternBase = {
fields: [
{ ...getField('nestedField.child') },
{ ...getField('nestedField.nestedChild.doublyNestedChild') },
@ -223,10 +231,10 @@ describe('Exception builder helpers', () => {
});
test('it returns all fields unfiletered if "item.nested" is not "child" or "parent"', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItem: FormattedBuilderEntry = getMockBuilderEntry();
const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'detection');
const expected: IIndexPattern = {
const expected: IndexPatternBase = {
fields: [...fields],
id: '1234',
title: 'logstash-*',
@ -236,7 +244,7 @@ describe('Exception builder helpers', () => {
});
describe('list type endpoint', () => {
let payloadIndexPattern: IIndexPattern = getMockIndexPattern();
let payloadIndexPattern = getMockIndexPattern();
beforeAll(() => {
payloadIndexPattern = {
@ -264,7 +272,7 @@ describe('Exception builder helpers', () => {
value: 'some value',
};
const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'endpoint');
const expected: IIndexPattern = {
const expected: IndexPatternBase = {
fields: [{ ...getEndpointField('file.Ext.code_signature.status'), name: 'status' }],
id: '1234',
title: 'logstash-*',
@ -273,33 +281,35 @@ describe('Exception builder helpers', () => {
});
test('it returns only parent nested field when "item.nested" is "parent" and nested parent field is not undefined', () => {
const field: FieldSpec = {
...getEndpointField('file.Ext.code_signature.status'),
esTypes: ['nested'],
name: 'file.Ext.code_signature',
} as FieldSpec;
const payloadItem: FormattedBuilderEntry = {
...getMockNestedParentBuilderEntry(),
field: {
...getEndpointField('file.Ext.code_signature.status'),
esTypes: ['nested'],
name: 'file.Ext.code_signature',
},
field,
};
const output = getFilteredIndexPatterns(payloadIndexPattern, payloadItem, 'endpoint');
const expected: IIndexPattern = {
fields: [
{
aggregatable: false,
count: 0,
esTypes: ['nested'],
name: 'file.Ext.code_signature',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: {
nested: {
path: 'file.Ext.code_signature',
},
const fieldsExpected: FieldSpec[] = [
{
aggregatable: false,
count: 0,
esTypes: ['nested'],
name: 'file.Ext.code_signature',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: {
nested: {
path: 'file.Ext.code_signature',
},
type: 'string',
},
],
type: 'string',
},
];
const expected: IndexPatternBase = {
fields: fieldsExpected,
id: '1234',
title: 'logstash-*',
};
@ -317,7 +327,7 @@ describe('Exception builder helpers', () => {
'endpoint',
filterIndexPatterns
);
const expected: IIndexPattern = {
const expected: IndexPatternBase = {
fields: [getEndpointField('file.Ext.code_signature.status')],
id: '1234',
title: 'logstash-*',
@ -333,30 +343,31 @@ describe('Exception builder helpers', () => {
'endpoint',
filterIndexPatterns
);
const expected: IIndexPattern = {
fields: [
{
aggregatable: false,
count: 0,
esTypes: ['keyword'],
name: 'file.path.caseless',
readFromDocValues: false,
scripted: false,
searchable: true,
type: 'string',
},
{
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'file.Ext.code_signature.status',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: { nested: { path: 'file.Ext.code_signature' } },
type: 'string',
},
],
const fieldsExpected: FieldSpec[] = [
{
aggregatable: false,
count: 0,
esTypes: ['keyword'],
name: 'file.path.caseless',
readFromDocValues: false,
scripted: false,
searchable: true,
type: 'string',
},
{
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'file.Ext.code_signature.status',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: { nested: { path: 'file.Ext.code_signature' } },
type: 'string',
},
];
const expected: IndexPatternBase = {
fields: fieldsExpected,
id: '1234',
title: 'logstash-*',
};
@ -626,7 +637,7 @@ describe('Exception builder helpers', () => {
describe('#getEntryOnFieldChange', () => {
test('it returns nested entry with single new subentry when "item.nested" is "parent"', () => {
const payloadItem: FormattedBuilderEntry = getMockNestedParentBuilderEntry();
const payloadIFieldType: IFieldType = getField('nestedField.child');
const payloadIFieldType = getField('nestedField.child');
const output = getEntryOnFieldChange(payloadItem, payloadIFieldType);
const expected: { updatedEntry: BuilderEntry & { id?: string }; index: number } = {
index: 0,
@ -663,7 +674,7 @@ describe('Exception builder helpers', () => {
parentIndex: 0,
},
};
const payloadIFieldType: IFieldType = getField('nestedField.child');
const payloadIFieldType = getField('nestedField.child');
const output = getEntryOnFieldChange(payloadItem, payloadIFieldType);
const expected: { updatedEntry: BuilderEntry & { id?: string }; index: number } = {
index: 0,
@ -688,7 +699,7 @@ describe('Exception builder helpers', () => {
test('it returns field of type "match" with updated field if not a nested entry', () => {
const payloadItem: FormattedBuilderEntry = getMockBuilderEntry();
const payloadIFieldType: IFieldType = getField('ip');
const payloadIFieldType = getField('ip');
const output = getEntryOnFieldChange(payloadItem, payloadIFieldType);
const expected: { updatedEntry: BuilderEntry & { id?: string }; index: number } = {
index: 0,
@ -1023,7 +1034,7 @@ describe('Exception builder helpers', () => {
describe('#getFormattedBuilderEntries', () => {
test('it returns formatted entry with field undefined if it unable to find a matching index pattern field', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItems: BuilderEntry[] = [getEntryMatchWithIdMock()];
const output = getFormattedBuilderEntries(payloadIndexPattern, payloadItems);
const expected: FormattedBuilderEntry[] = [
@ -1042,26 +1053,37 @@ describe('Exception builder helpers', () => {
});
test('it returns formatted entries when no nested entries exist', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItems: BuilderEntry[] = [
{ ...getEntryMatchWithIdMock(), field: 'ip', value: 'some ip' },
{ ...getEntryMatchAnyWithIdMock(), field: 'extension', value: ['some extension'] },
];
const output = getFormattedBuilderEntries(payloadIndexPattern, payloadItems);
const field1: FieldSpec = {
aggregatable: true,
count: 0,
esTypes: ['ip'],
name: 'ip',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'ip',
};
const field2: FieldSpec = {
aggregatable: true,
count: 0,
esTypes: ['keyword'],
name: 'extension',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'string',
};
const expected: FormattedBuilderEntry[] = [
{
correspondingKeywordField: undefined,
entryIndex: 0,
field: {
aggregatable: true,
count: 0,
esTypes: ['ip'],
name: 'ip',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'ip',
},
field: field1,
id: '123',
nested: undefined,
operator: isOperator,
@ -1071,16 +1093,7 @@ describe('Exception builder helpers', () => {
{
correspondingKeywordField: undefined,
entryIndex: 1,
field: {
aggregatable: true,
count: 0,
esTypes: ['keyword'],
name: 'extension',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'string',
},
field: field2,
id: '123',
nested: undefined,
operator: isOneOfOperator,
@ -1092,7 +1105,7 @@ describe('Exception builder helpers', () => {
});
test('it returns formatted entries when nested entries exist', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadParent: EntryNested = {
...getEntryNestedWithIdMock(),
entries: [{ ...getEntryMatchWithIdMock(), field: 'child' }],
@ -1104,20 +1117,43 @@ describe('Exception builder helpers', () => {
];
const output = getFormattedBuilderEntries(payloadIndexPattern, payloadItems);
const field1: FieldSpec = {
aggregatable: true,
count: 0,
esTypes: ['ip'],
name: 'ip',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'ip',
};
const field2: FieldSpec = {
aggregatable: false,
esTypes: ['nested'],
name: 'nestedField',
searchable: false,
type: 'string',
};
const field3: FieldSpec = {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'child',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: {
nested: {
path: 'nestedField',
},
},
type: 'string',
};
const expected: FormattedBuilderEntry[] = [
{
correspondingKeywordField: undefined,
entryIndex: 0,
field: {
aggregatable: true,
count: 0,
esTypes: ['ip'],
name: 'ip',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'ip',
},
field: field1,
id: '123',
nested: undefined,
operator: isOperator,
@ -1127,13 +1163,7 @@ describe('Exception builder helpers', () => {
{
correspondingKeywordField: undefined,
entryIndex: 1,
field: {
aggregatable: false,
esTypes: ['nested'],
name: 'nestedField',
searchable: false,
type: 'string',
},
field: field2,
id: '123',
nested: 'parent',
operator: isOperator,
@ -1143,21 +1173,7 @@ describe('Exception builder helpers', () => {
{
correspondingKeywordField: undefined,
entryIndex: 0,
field: {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'child',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: {
nested: {
path: 'nestedField',
},
},
type: 'string',
},
field: field3,
id: '123',
nested: 'child',
operator: isOperator,
@ -1248,7 +1264,7 @@ describe('Exception builder helpers', () => {
describe('#getFormattedBuilderEntry', () => {
test('it returns entry with a value for "correspondingKeywordField" when "item.field" is of type "text" and matching keyword field exists', () => {
const payloadIndexPattern: IIndexPattern = {
const payloadIndexPattern: IndexPatternBase = {
...getMockIndexPattern(),
fields: [
...fields,
@ -1276,19 +1292,20 @@ describe('Exception builder helpers', () => {
undefined,
undefined
);
const field: FieldSpec = {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'machine.os.raw.text',
readFromDocValues: true,
scripted: false,
searchable: false,
type: 'string',
};
const expected: FormattedBuilderEntry = {
correspondingKeywordField: getField('machine.os.raw'),
entryIndex: 0,
field: {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'machine.os.raw.text',
readFromDocValues: true,
scripted: false,
searchable: false,
type: 'string',
},
field,
id: '123',
nested: undefined,
operator: isOperator,
@ -1299,7 +1316,7 @@ describe('Exception builder helpers', () => {
});
test('it returns "FormattedBuilderEntry" with value "nested" of "child" when "parent" and "parentIndex" are defined', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItem: BuilderEntry = { ...getEntryMatchWithIdMock(), field: 'child' };
const payloadParent: EntryNested = {
...getEntryNestedWithIdMock(),
@ -1313,24 +1330,25 @@ describe('Exception builder helpers', () => {
payloadParent,
1
);
const field: FieldSpec = {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'child',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: {
nested: {
path: 'nestedField',
},
},
type: 'string',
};
const expected: FormattedBuilderEntry = {
correspondingKeywordField: undefined,
entryIndex: 0,
field: {
aggregatable: false,
count: 0,
esTypes: ['text'],
name: 'child',
readFromDocValues: false,
scripted: false,
searchable: true,
subType: {
nested: {
path: 'nestedField',
},
},
type: 'string',
},
field,
id: '123',
nested: 'child',
operator: isOperator,
@ -1349,7 +1367,7 @@ describe('Exception builder helpers', () => {
});
test('it returns non nested "FormattedBuilderEntry" when "parent" and "parentIndex" are not defined', () => {
const payloadIndexPattern: IIndexPattern = getMockIndexPattern();
const payloadIndexPattern = getMockIndexPattern();
const payloadItem: BuilderEntry = {
...getEntryMatchWithIdMock(),
field: 'ip',
@ -1362,19 +1380,20 @@ describe('Exception builder helpers', () => {
undefined,
undefined
);
const field: FieldSpec = {
aggregatable: true,
count: 0,
esTypes: ['ip'],
name: 'ip',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'ip',
};
const expected: FormattedBuilderEntry = {
correspondingKeywordField: undefined,
entryIndex: 0,
field: {
aggregatable: true,
count: 0,
esTypes: ['ip'],
name: 'ip',
readFromDocValues: true,
scripted: false,
searchable: true,
type: 'ip',
},
field,
id: '123',
nested: undefined,
operator: isOperator,

View file

@ -41,14 +41,14 @@ import { getEntryMatchMock } from '../../../../../lists/common/schemas/types/ent
import { getCommentsArrayMock } from '../../../../../lists/common/schemas/types/comment.mock';
import { fields } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks';
import { ENTRIES, OLD_DATE_RELATIVE_TO_DATE_NOW } from '../../../../../lists/common/constants.mock';
import { IFieldType, IIndexPattern } from 'src/plugins/data/common';
import { CodeSignature } from '../../../../common/ecs/file';
import { IndexPatternBase } from '@kbn/es-query';
jest.mock('uuid', () => ({
v4: jest.fn().mockReturnValue('123'),
}));
const getMockIndexPattern = (): IIndexPattern => ({
const getMockIndexPattern = (): IndexPatternBase => ({
fields,
id: '1234',
title: 'logstash-*',
@ -91,9 +91,6 @@ const mockLinuxEndpointFields = [
},
];
export const getEndpointField = (name: string) =>
mockEndpointFields.find((field) => field.name === name) as IFieldType;
describe('Exception helpers', () => {
beforeEach(() => {
moment.tz.setDefault('UTC');
@ -367,7 +364,7 @@ describe('Exception helpers', () => {
name: 'nested.field',
},
],
} as IIndexPattern;
} as IndexPatternBase;
test('it should return false with an empty array', () => {
const payload: ExceptionListItemSchema[] = [];

View file

@ -33,10 +33,10 @@ import {
addIdToEntries,
ExceptionsBuilderExceptionItem,
} from '@kbn/securitysolution-list-utils';
import { IndexPatternBase } from '@kbn/es-query';
import * as i18n from './translations';
import { AlertData, Flattened } from './types';
import { IIndexPattern } from '../../../../../../../src/plugins/data/common';
import { Ecs } from '../../../../common/ecs';
import { CodeSignature } from '../../../../common/ecs/file';
import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
@ -46,10 +46,10 @@ import exceptionableEndpointFields from './exceptionable_endpoint_fields.json';
import exceptionableEndpointEventFields from './exceptionable_endpoint_event_fields.json';
export const filterIndexPatterns = (
patterns: IIndexPattern,
patterns: IndexPatternBase,
type: ExceptionListType,
osTypes?: OsTypeArray
): IIndexPattern => {
): IndexPatternBase => {
switch (type) {
case 'endpoint':
const osFilterForEndpoint: (name: string) => boolean = osTypes?.includes('linux')
@ -634,7 +634,7 @@ export const getPrepopulatedMemoryShellcodeException = ({
*/
export const entryHasNonEcsType = (
exceptionItems: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>,
indexPatterns: IIndexPattern
indexPatterns: IndexPatternBase
): boolean => {
const doesFieldNameExist = (exceptionEntry: Entry): boolean => {
return indexPatterns.fields.some(({ name }) => name === exceptionEntry.field);

View file

@ -10,7 +10,8 @@ import { EuiFormRow, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import styled from 'styled-components';
import { FieldComponent } from '@kbn/securitysolution-autocomplete';
import { IFieldType, IndexPattern } from '../../../../../../../src/plugins/data/common';
import { IndexPatternFieldBase } from '@kbn/es-query';
import { IndexPattern } from '../../../../../../../src/plugins/data/common';
import { FormattedEntry, Entry } from './types';
import * as i18n from './translations';
import { getEntryOnFieldChange, getEntryOnThreatFieldChange } from './helpers';
@ -40,7 +41,7 @@ export const EntryItem: React.FC<EntryItemProps> = ({
onChange,
}): JSX.Element => {
const handleFieldChange = useCallback(
([newField]: IFieldType[]): void => {
([newField]: IndexPatternFieldBase[]): void => {
const { updatedEntry, index } = getEntryOnFieldChange(entry, newField);
onChange(updatedEntry, index);
},
@ -48,7 +49,7 @@ export const EntryItem: React.FC<EntryItemProps> = ({
);
const handleThreatFieldChange = useCallback(
([newField]: IFieldType[]): void => {
([newField]: IndexPatternFieldBase[]): void => {
const { updatedEntry, index } = getEntryOnThreatFieldChange(entry, newField);
onChange(updatedEntry, index);
},

View file

@ -10,7 +10,7 @@ import {
getField,
} from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks';
import { Entry, EmptyEntry, ThreatMapEntries, FormattedEntry } from './types';
import { IndexPattern } from '../../../../../../../src/plugins/data/common';
import { FieldSpec, IndexPattern } from '../../../../../../../src/plugins/data/common';
import moment from 'moment-timezone';
import {
@ -88,7 +88,7 @@ describe('Helpers', () => {
searchable: false,
aggregatable: false,
readFromDocValues: true,
},
} as FieldSpec,
type: 'mapping',
value: undefined,
};
@ -130,7 +130,7 @@ describe('Helpers', () => {
searchable: true,
aggregatable: true,
readFromDocValues: false,
},
} as FieldSpec,
value: undefined,
type: 'mapping',
},
@ -156,7 +156,7 @@ describe('Helpers', () => {
searchable: true,
aggregatable: true,
readFromDocValues: false,
},
} as FieldSpec,
value: {
name: 'machine.os',
type: 'string',
@ -166,7 +166,7 @@ describe('Helpers', () => {
searchable: true,
aggregatable: true,
readFromDocValues: false,
},
} as FieldSpec,
type: 'mapping',
},
];
@ -192,7 +192,7 @@ describe('Helpers', () => {
searchable: true,
aggregatable: true,
readFromDocValues: false,
},
} as FieldSpec,
type: 'mapping',
value: {
name: 'machine.os',
@ -203,7 +203,7 @@ describe('Helpers', () => {
searchable: true,
aggregatable: true,
readFromDocValues: false,
},
} as FieldSpec,
entryIndex: 0,
},
{

View file

@ -10,7 +10,8 @@ import { i18n } from '@kbn/i18n';
import { addIdToItem } from '@kbn/securitysolution-utils';
import { ThreatMap, threatMap, ThreatMapping } from '@kbn/securitysolution-io-ts-alerting-types';
import { IndexPattern, IFieldType } from '../../../../../../../src/plugins/data/common';
import { IndexPatternFieldBase } from '@kbn/es-query';
import { IndexPattern } from '../../../../../../../src/plugins/data/common';
import { Entry, FormattedEntry, ThreatMapEntries, EmptyEntry } from './types';
import { ValidationFunc } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
import { ERROR_CODE } from '../../../../../../../src/plugins/es_ui_shared/static/forms/helpers/field_validators/types';
@ -90,7 +91,7 @@ export const getUpdatedEntriesOnDelete = (
*/
export const getEntryOnFieldChange = (
item: FormattedEntry,
newField: IFieldType
newField: IndexPatternFieldBase
): { updatedEntry: Entry; index: number } => {
const { entryIndex } = item;
return {
@ -113,7 +114,7 @@ export const getEntryOnFieldChange = (
*/
export const getEntryOnThreatFieldChange = (
item: FormattedEntry,
newField: IFieldType
newField: IndexPatternFieldBase
): { updatedEntry: Entry; index: number } => {
const { entryIndex } = item;
return {

View file

@ -5,14 +5,14 @@
* 2.0.
*/
import { IndexPatternFieldBase } from '@kbn/es-query';
import { ThreatMap, ThreatMapEntry } from '@kbn/securitysolution-io-ts-alerting-types';
import { IFieldType } from '../../../../../../../src/plugins/data/common';
export interface FormattedEntry {
id: string;
field: IFieldType | undefined;
field: IndexPatternFieldBase | undefined;
type: 'mapping';
value: IFieldType | undefined;
value: IndexPatternFieldBase | undefined;
entryIndex: number;
}

View file

@ -8,15 +8,14 @@
import React, { useCallback, useMemo } from 'react';
import { EuiFormRow } from '@elastic/eui';
import { FieldComponent } from '@kbn/securitysolution-autocomplete';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields';
import { IIndexPattern } from '../../../../../../../../src/plugins/data/common';
interface AutocompleteFieldProps {
dataTestSubj: string;
field: FieldHook;
idAria: string;
indices: IIndexPattern;
indices: IndexPatternBase;
isDisabled: boolean;
fieldType: string;
placeholder?: string;
@ -32,7 +31,7 @@ export const AutocompleteField = ({
placeholder,
}: AutocompleteFieldProps) => {
const handleFieldChange = useCallback(
([newField]: IFieldType[]): void => {
([newField]: IndexPatternFieldBase[]): void => {
// TODO: Update onChange type in FieldComponent as newField can be undefined
field.setValue(newField?.name ?? '');
},

View file

@ -21,11 +21,10 @@ import styled from 'styled-components';
import { noop } from 'lodash/fp';
import { RiskScoreMapping } from '@kbn/securitysolution-io-ts-alerting-types';
import { FieldComponent } from '@kbn/securitysolution-autocomplete';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import * as i18n from './translations';
import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
import { AboutStepRiskScore } from '../../../pages/detection_engine/rules/types';
import { IFieldType } from '../../../../../../../../src/plugins/data/common/index_patterns/fields';
import { IIndexPattern } from '../../../../../../../../src/plugins/data/common/index_patterns';
const NestedContent = styled.div`
margin-left: 24px;
@ -47,7 +46,7 @@ interface RiskScoreFieldProps {
dataTestSubj: string;
field: FieldHook<AboutStepRiskScore>;
idAria: string;
indices: IIndexPattern;
indices: IndexPatternBase;
isDisabled: boolean;
placeholder?: string;
}
@ -79,7 +78,7 @@ export const RiskScoreField = ({
);
const handleRiskScoreMappingChange = useCallback(
([newField]: IFieldType[]): void => {
([newField]: IndexPatternFieldBase[]): void => {
setValue({
value,
isMappingChecked,
@ -232,14 +231,17 @@ export const RiskScoreField = ({
};
/**
* Looks for field metadata (IFieldType) in existing index pattern.
* If specified field doesn't exist, returns a stub IFieldType created based on the mapping --
* Looks for field metadata (IndexPatternFieldBase) in existing index pattern.
* If specified field doesn't exist, returns a stub IndexPatternFieldBase created based on the mapping --
* because the field might not have been indexed yet, but we still need to display the mapping.
*
* @param mapping Mapping of a specified field name to risk score.
* @param pattern Existing index pattern.
*/
const getFieldTypeByMapping = (mapping: RiskScoreMapping, pattern: IIndexPattern): IFieldType => {
const getFieldTypeByMapping = (
mapping: RiskScoreMapping,
pattern: IndexPatternBase
): IndexPatternFieldBase => {
const field = mapping?.[0]?.field ?? '';
const [knownFieldType] = pattern.fields.filter(({ name }) => field != null && field === name);
return knownFieldType ?? { name: field, type: 'number' };

View file

@ -29,14 +29,11 @@ import {
AutocompleteFieldMatchComponent,
} from '@kbn/securitysolution-autocomplete';
import { IndexPatternBase, IndexPatternFieldBase } from '@kbn/es-query';
import * as i18n from './translations';
import { FieldHook } from '../../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib';
import { SeverityOptionItem } from '../step_about_rule/data';
import { AboutStepSeverity } from '../../../pages/detection_engine/rules/types';
import {
IFieldType,
IIndexPattern,
} from '../../../../../../../../src/plugins/data/common/index_patterns';
import { useKibana } from '../../../../common/lib/kibana';
const NestedContent = styled.div`
@ -59,7 +56,7 @@ interface SeverityFieldProps {
dataTestSubj: string;
field: FieldHook<AboutStepSeverity>;
idAria: string;
indices: IIndexPattern;
indices: IndexPatternBase;
isDisabled: boolean;
options: SeverityOptionItem[];
}
@ -88,7 +85,7 @@ export const SeverityField = ({
);
const handleFieldChange = useCallback(
(index: number, severity: Severity, [newField]: IFieldType[]): void => {
(index: number, severity: Severity, [newField]: IndexPatternFieldBase[]): void => {
const newMappingItems: SeverityMapping = [
{
...mapping[index],
@ -298,8 +295,8 @@ export const SeverityField = ({
};
/**
* Looks for field metadata (IFieldType) in existing index pattern.
* If specified field doesn't exist, returns a stub IFieldType created based on the mapping --
* Looks for field metadata (IndexPatternFieldBase) in existing index pattern.
* If specified field doesn't exist, returns a stub IndexPatternFieldBase created based on the mapping --
* because the field might not have been indexed yet, but we still need to display the mapping.
*
* @param mapping Mapping of a specified field name + value to a certain severity value.
@ -307,8 +304,8 @@ export const SeverityField = ({
*/
const getFieldTypeByMapping = (
mapping: SeverityMappingItem,
pattern: IIndexPattern
): IFieldType => {
pattern: IndexPatternBase
): IndexPatternFieldBase => {
const { field } = mapping;
const [knownFieldType] = pattern.fields.filter(({ name }) => field === name);
return knownFieldType ?? { name: field, type: 'string' };