[Security Solution][Timeline] Remove BrowserField type (#188923)

## Summary

This PR removes `BrowserField` type, as it is now replaced with data
plugins' `FieldSpec`.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Luke G 2024-08-02 11:03:13 +02:00 committed by GitHub
parent 920a40c449
commit efb71fa11f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 59 additions and 81 deletions

View file

@ -9,8 +9,9 @@ import { i18n } from '@kbn/i18n';
import type { EuiDataGridColumnActions } from '@elastic/eui';
import { keyBy } from 'lodash/fp';
import React from 'react';
import type { FieldSpec } from '@kbn/data-plugin/common';
import { BrowserFields } from '@kbn/timelines-plugin/common';
import { BrowserField, BrowserFields } from '@kbn/timelines-plugin/common';
import { DEFAULT_TABLE_COLUMN_MIN_WIDTH, DEFAULT_TABLE_DATE_COLUMN_MIN_WIDTH } from '../constants';
import { defaultColumnHeaderType } from '../../../store/data_table/defaults';
import { ColumnHeaderOptions } from '../../../common/types';
@ -25,7 +26,7 @@ export const allowSorting = ({
browserField,
fieldName,
}: {
browserField: Partial<BrowserField> | undefined;
browserField: Partial<FieldSpec> | undefined;
fieldName: string;
}): boolean => {
const isAggregatable = browserField?.aggregatable ?? false;
@ -94,8 +95,8 @@ export const allowSorting = ({
return isAllowlistedNonBrowserField || isAggregatable;
};
const getAllBrowserFields = (browserFields: BrowserFields): Array<Partial<BrowserField>> =>
Object.values(browserFields).reduce<Array<Partial<BrowserField>>>(
const getAllBrowserFields = (browserFields: BrowserFields): Array<Partial<FieldSpec>> =>
Object.values(browserFields).reduce<Array<Partial<FieldSpec>>>(
(acc, namespace) => [
...acc,
...Object.values(namespace.fields != null ? namespace.fields : {}),
@ -105,8 +106,7 @@ const getAllBrowserFields = (browserFields: BrowserFields): Array<Partial<Browse
const getAllFieldsByName = (
browserFields: BrowserFields
): { [fieldName: string]: Partial<BrowserField> } =>
keyBy('name', getAllBrowserFields(browserFields));
): { [fieldName: string]: Partial<FieldSpec> } => keyBy('name', getAllBrowserFields(browserFields));
/**
* Valid built-in schema types for the `schema` property of `EuiDataGridColumn`
@ -204,7 +204,7 @@ export const getColumnHeaders = (
const headersToMap = isEventRenderedView ? eventRenderedViewColumns : headers;
return headersToMap
? headersToMap.map((header) => {
const browserField: Partial<BrowserField> | undefined = browserFieldByName[header.id];
const browserField: Partial<FieldSpec> | undefined = browserFieldByName[header.id];
// augment the header with metadata from browserFields:
const augmentedHeader = {

View file

@ -20,6 +20,7 @@
"@kbn/i18n-react",
"@kbn/ui-actions-plugin",
"@kbn/data-views-plugin",
"@kbn/field-formats-plugin"
"@kbn/field-formats-plugin",
"@kbn/data-plugin"
]
}

View file

@ -11,7 +11,6 @@ export type {
BeatFields,
IndexFieldsStrategyRequest,
IndexFieldsStrategyResponse,
BrowserField,
BrowserFields,
} from '@kbn/timelines-plugin/common';
export { EMPTY_BROWSER_FIELDS, EMPTY_INDEX_FIELDS } from '@kbn/timelines-plugin/common';

View file

@ -8,13 +8,14 @@ import { keyBy } from 'lodash/fp';
import type { DropResult } from '@hello-pangea/dnd';
import type { Dispatch } from 'redux';
import type { ActionCreator } from 'typescript-fsa';
import type { FieldSpec } from '@kbn/data-plugin/common';
import { getFieldIdFromDraggable, getProviderIdFromDraggable } from '@kbn/securitysolution-t-grid';
import { TableId } from '@kbn/securitysolution-data-table';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
import { getScopedActions } from '../../../helpers';
import type { ColumnHeaderOptions } from '../../../../common/types';
import type { BrowserField, BrowserFields } from '../../../../common/search_strategy';
import type { BrowserFields } from '../../../../common/search_strategy';
import { dragAndDropActions } from '../../store/actions';
import type { IdToDataProvider } from '../../store/drag_and_drop/model';
import { addContentToTimeline } from '../../../timelines/components/timeline/data_providers/helpers';
@ -184,8 +185,8 @@ export const allowTopN = ({
return isAllowlistedNonBrowserField || (isAggregatable && isAllowedType);
};
const getAllBrowserFields = (browserFields: BrowserFields): Array<Partial<BrowserField>> =>
Object.values(browserFields).reduce<Array<Partial<BrowserField>>>(
const getAllBrowserFields = (browserFields: BrowserFields): Array<Partial<FieldSpec>> =>
Object.values(browserFields).reduce<Array<Partial<FieldSpec>>>(
(acc, namespace) => [
...acc,
...Object.values(namespace.fields != null ? namespace.fields : {}),
@ -195,8 +196,7 @@ const getAllBrowserFields = (browserFields: BrowserFields): Array<Partial<Browse
const getAllFieldsByName = (
browserFields: BrowserFields
): { [fieldName: string]: Partial<BrowserField> } =>
keyBy('name', getAllBrowserFields(browserFields));
): { [fieldName: string]: Partial<FieldSpec> } => keyBy('name', getAllBrowserFields(browserFields));
const linkFields: Record<string, string> = {
'kibana.alert.rule.name': 'kibana.alert.rule.uuid',

View file

@ -11,6 +11,8 @@ import type { Filter } from '@kbn/es-query';
import { escapeDataProviderId } from '@kbn/securitysolution-t-grid';
import { isArray, isEmpty, isString } from 'lodash/fp';
import { useMemo } from 'react';
import type { FieldSpec } from '@kbn/data-plugin/common';
import {
AGENT_STATUS_FIELD_NAME,
EVENT_MODULE_FIELD_NAME,
@ -29,7 +31,6 @@ import { EVENT_DURATION_FIELD_NAME } from '../../../../timelines/components/dura
import { getDisplayValue } from '../../../../timelines/components/timeline/data_providers/helpers';
import { PORT_NAMES } from '../../../../explore/network/components/port/helpers';
import { INDICATOR_REFERENCE } from '../../../../../common/cti/constants';
import type { BrowserField } from '../../../containers/source';
import type { DataProvider, DataProvidersAnd, QueryOperator } from '../../../../../common/types';
import { IS_OPERATOR } from '../../../../../common/types';
@ -38,7 +39,7 @@ export interface UseActionCellDataProvider {
eventId?: string;
field: string;
fieldFormat?: string;
fieldFromBrowserField?: Partial<BrowserField>;
fieldFromBrowserField?: Partial<FieldSpec>;
fieldType?: string;
isObjectArray?: boolean;
linkValue?: string | null;

View file

@ -5,10 +5,10 @@
* 2.0.
*/
import type { BrowserField } from '../../containers/source';
import type { FieldSpec } from '@kbn/data-plugin/common';
import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
export type EventFieldsData = BrowserField & TimelineEventsDetailsItem;
export type EventFieldsData = FieldSpec & TimelineEventsDetailsItem;
export interface FieldsData {
field: string;
@ -20,7 +20,7 @@ export interface FieldsData {
export interface EnrichedFieldInfo {
data: FieldsData | EventFieldsData;
eventId: string;
fieldFromBrowserField?: Partial<BrowserField>;
fieldFromBrowserField?: Partial<FieldSpec>;
scopeId: string;
values: string[] | null | undefined;
linkValue?: string;

View file

@ -7,10 +7,10 @@
import { useMemo } from 'react';
import type { DataViewFieldBase } from '@kbn/es-query';
import type { FieldSpec } from '@kbn/data-plugin/common';
import { getTermsAggregationFields } from '../../../../detection_engine/rule_creation_ui/components/step_define_rule/utils';
import { useRuleFields } from '../../../../detection_engine/rule_management/logic/use_rule_fields';
import type { BrowserField } from '../../../containers/source';
import { useMlCapabilities } from './use_ml_capabilities';
import { useMlRuleValidations } from './use_ml_rule_validations';
import { hasMlAdminPermissions } from '../../../../../common/machine_learning/has_ml_admin_permissions';
@ -21,7 +21,7 @@ export interface UseMlRuleConfigReturn {
hasMlLicense: boolean;
loading: boolean;
mlFields: DataViewFieldBase[];
mlSuppressionFields: BrowserField[];
mlSuppressionFields: FieldSpec[];
allJobsStarted: boolean;
}
@ -46,7 +46,7 @@ export const useMLRuleConfig = ({
machineLearningJobId,
});
const mlSuppressionFields = useMemo(
() => getTermsAggregationFields(mlFields as BrowserField[]),
() => getTermsAggregationFields(mlFields as FieldSpec[]),
[mlFields]
);

View file

@ -9,8 +9,8 @@ import { isEmpty, isEqual, keyBy, pick } from 'lodash/fp';
import memoizeOne from 'memoize-one';
import { useCallback, useEffect, useRef, useState } from 'react';
import type { DataViewBase } from '@kbn/es-query';
import type { BrowserField, BrowserFields } from '@kbn/timelines-plugin/common';
import type { IIndexPatternFieldList } from '@kbn/data-views-plugin/common';
import type { BrowserFields } from '@kbn/timelines-plugin/common';
import type { FieldSpec, IIndexPatternFieldList } from '@kbn/data-views-plugin/common';
import type { DataViewSpec } from '@kbn/data-views-plugin/public';
import { useKibana } from '../../lib/kibana';
@ -19,10 +19,10 @@ import { getDataViewStateFromIndexFields } from './use_data_view';
import { useAppToasts } from '../../hooks/use_app_toasts';
import type { ENDPOINT_FIELDS_SEARCH_STRATEGY } from '../../../../common/endpoint/constants';
export type { BrowserField, BrowserFields };
export type { BrowserFields };
export function getAllBrowserFields(browserFields: BrowserFields): Array<Partial<BrowserField>> {
const result: Array<Partial<BrowserField>> = [];
export function getAllBrowserFields(browserFields: BrowserFields): Array<Partial<FieldSpec>> {
const result: Array<Partial<FieldSpec>> = [];
for (const namespace of Object.values(browserFields)) {
if (namespace.fields) {
result.push(...Object.values(namespace.fields));
@ -38,8 +38,7 @@ export function getAllBrowserFields(browserFields: BrowserFields): Array<Partial
*/
export const getAllFieldsByName = (
browserFields: BrowserFields
): { [fieldName: string]: Partial<BrowserField> } =>
keyBy('name', getAllBrowserFields(browserFields));
): { [fieldName: string]: Partial<FieldSpec> } => keyBy('name', getAllBrowserFields(browserFields));
export const getIndexFields = memoizeOne(
(title: string, fields: IIndexPatternFieldList): DataViewBase =>

View file

@ -9,9 +9,10 @@ import { useCallback, useRef } from 'react';
import type { Subscription } from 'rxjs';
import { useDispatch } from 'react-redux';
import memoizeOne from 'memoize-one';
import type { BrowserField, BrowserFields } from '@kbn/timelines-plugin/common';
import type { BrowserFields } from '@kbn/timelines-plugin/common';
import { getCategory } from '@kbn/triggers-actions-ui-plugin/public';
import type { DataViewSpec } from '@kbn/data-views-plugin/public';
import type { FieldCategory } from '@kbn/timelines-plugin/common/search_strategy';
import { useKibana } from '../../lib/kibana';
import { sourcererActions } from '../../../sourcerer/store';
@ -28,10 +29,7 @@ export type IndexFieldSearch = (param: {
skipScopeUpdate?: boolean;
}) => Promise<void>;
type DangerCastForBrowserFieldsMutation = Record<
string,
Omit<BrowserField, 'fields'> & { fields: Record<string, BrowserField> }
>;
type DangerCastForBrowserFieldsMutation = Record<string, FieldCategory>;
interface DataViewInfo {
/**
* @deprecated use fields list on dataview / "indexPattern"
@ -62,7 +60,7 @@ export const getDataViewStateFromIndexFields = memoizeOne(
}
const categoryFields = browserFields[category].fields;
if (categoryFields) {
categoryFields[name] = field as BrowserField;
categoryFields[name] = field;
}
}
return { browserFields: browserFields as DangerCastForBrowserFieldsMutation };

View file

@ -24,7 +24,7 @@ import React, { memo, useCallback, useState, useEffect, useMemo, useRef } from '
import styled from 'styled-components';
import { i18n as i18nCore } from '@kbn/i18n';
import { isEqual, isEmpty } from 'lodash';
import type { FieldSpec } from '@kbn/data-views-plugin/common';
import type { FieldSpec } from '@kbn/data-plugin/common';
import usePrevious from 'react-use/lib/usePrevious';
import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
import { useQueryClient } from '@tanstack/react-query';
@ -83,7 +83,6 @@ import {
import { EqlQueryBar } from '../eql_query_bar';
import { DataViewSelector } from '../data_view_selector';
import { ThreatMatchInput } from '../threatmatch_input';
import type { BrowserField } from '../../../../common/containers/source';
import { useFetchIndex } from '../../../../common/containers/source';
import { NewTermsFields } from '../new_terms_fields';
import { ScheduleItem } from '../../../rule_creation/components/schedule_item_form';
@ -266,12 +265,12 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
[form]
);
const [aggFields, setAggregatableFields] = useState<BrowserField[]>([]);
const [aggFields, setAggregatableFields] = useState<FieldSpec[]>([]);
useEffect(() => {
const { fields } = indexPattern;
/**
* Typecasting to BrowserField because fields is
* Typecasting to FieldSpec because fields is
* typed as DataViewFieldBase[] which does not have
* the 'aggregatable' property, however the type is incorrect
*
@ -279,10 +278,10 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({
* We will need to determine where these types are defined and
* figure out where the discrepency is.
*/
setAggregatableFields(aggregatableFields(fields as BrowserField[]));
setAggregatableFields(aggregatableFields(fields as FieldSpec[]));
}, [indexPattern]);
const termsAggregationFields: BrowserField[] = useMemo(
const termsAggregationFields: FieldSpec[] = useMemo(
() => getTermsAggregationFields(aggFields),
[aggFields]
);

View file

@ -6,8 +6,7 @@
*/
import type { Type } from '@kbn/securitysolution-io-ts-alerting-types';
import type { BrowserField } from '../../../../common/containers/source';
import type { FieldSpec } from '@kbn/data-plugin/common';
import { CUSTOM_QUERY_REQUIRED, EQL_QUERY_REQUIRED, ESQL_QUERY_REQUIRED } from './translations';
@ -19,7 +18,7 @@ import { isEqlRule, isEsqlRule } from '../../../../../common/detection_engine/ut
* Keyword, Numeric, ip, boolean, or binary.
* https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html
*/
export const getTermsAggregationFields = (fields: BrowserField[]): BrowserField[] => {
export const getTermsAggregationFields = (fields: FieldSpec[]): FieldSpec[] => {
// binary types is excluded, as binary field has property aggregatable === false
const allowedTypesSet = new Set(['string', 'number', 'ip', 'boolean']);

View file

@ -7,7 +7,7 @@
import React from 'react';
import { renderHook } from '@testing-library/react-hooks';
import type { BrowserField } from '@kbn/timelines-plugin/common';
import type { FieldSpec } from '@kbn/data-plugin/common';
import type { GetAggregatableFields, UseInspectButtonParams } from './hooks';
import { getAggregatableFields, useInspectButton, useStackByFields } from './hooks';
@ -57,7 +57,7 @@ describe('getAggregatableFields', () => {
expect(
getAggregatableFields(
{ [field]: mockBrowserFields?.destination?.fields?.[field] as Partial<BrowserField> },
{ [field]: mockBrowserFields?.destination?.fields?.[field] as Partial<FieldSpec> },
useLensCompatibleFields
)
).toHaveLength(1);

View file

@ -9,8 +9,8 @@ import { useCallback, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import type { EuiComboBoxOptionOption } from '@elastic/eui';
import type { IFieldSubTypeNested } from '@kbn/es-query';
import type { FieldSpec } from '@kbn/data-plugin/common';
import type { BrowserField } from '@kbn/timelines-plugin/common';
import { i18n } from '@kbn/i18n';
import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
import type { GlobalTimeArgs } from '../../../../common/containers/use_global_time';
@ -64,13 +64,13 @@ export const useInspectButton = ({
}, [setQuery, loading, response, request, refetch, uniqueQueryId, deleteQuery, searchSessionId]);
};
export function isDataViewFieldSubtypeNested(field: Partial<BrowserField>) {
export function isDataViewFieldSubtypeNested(field: Partial<FieldSpec>) {
const subTypeNested = field?.subType as IFieldSubTypeNested;
return !!subTypeNested?.nested?.path;
}
export interface GetAggregatableFields {
[fieldName: string]: Partial<BrowserField>;
[fieldName: string]: Partial<FieldSpec>;
}
export function getAggregatableFields(

View file

@ -7,8 +7,7 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import type { BrowserField } from '@kbn/timelines-plugin/common';
import type { FieldSpec } from '@kbn/data-plugin/common';
import type { EventFieldsData } from '../../../../common/components/event_details/types';
import { TableFieldValueCell } from './table_field_value_cell';
import { TestProviders } from '../../../../common/mock';
@ -88,7 +87,7 @@ describe('TableFieldValueCell', () => {
};
const messageValues = ['Endpoint network event'];
const messageFieldFromBrowserField: BrowserField = {
const messageFieldFromBrowserField: FieldSpec = {
aggregatable: false,
name: 'message',
readFromDocValues: false,
@ -121,8 +120,8 @@ describe('TableFieldValueCell', () => {
});
});
describe('when `BrowserField` metadata IS available', () => {
const hostIpFieldFromBrowserField: BrowserField = {
describe('when `FieldSpec` metadata IS available', () => {
const hostIpFieldFromBrowserField: FieldSpec = {
aggregatable: true,
name: 'host.ip',
readFromDocValues: false,

View file

@ -7,7 +7,7 @@
import React, { memo } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import type { BrowserField } from '@kbn/timelines-plugin/common';
import type { FieldSpec } from '@kbn/data-plugin/common';
import { getFieldFormat } from '../../../../common/components/event_details/get_field_format';
import type { EventFieldsData } from '../../../../common/components/event_details/types';
import { OverflowField } from '../../../../common/components/tables/helpers';
@ -30,7 +30,7 @@ export interface FieldValueCellProps {
/**
* Field retrieved from the BrowserField
*/
fieldFromBrowserField?: Partial<BrowserField>;
fieldFromBrowserField?: Partial<FieldSpec>;
/**
* Value of the link field if it exists. Allows to navigate to other pages like host, user, network...
*/

View file

@ -13,11 +13,8 @@ import { type EuiBasicTableColumn, EuiText, EuiInMemoryTable, useEuiFontSize } f
import { i18n } from '@kbn/i18n';
import { dataTableSelectors, tableDefaults } from '@kbn/securitysolution-data-table';
import { getCategory } from '@kbn/triggers-actions-ui-plugin/public';
import type {
BrowserField,
BrowserFields,
TimelineEventsDetailsItem,
} from '@kbn/timelines-plugin/common';
import type { BrowserFields, TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
import type { FieldSpec } from '@kbn/data-plugin/common';
import { TableFieldNameCell } from '../components/table_field_name_cell';
import { TableFieldValueCell } from '../components/table_field_value_cell';
import { TABLE_TAB_CONTENT_TEST_ID, TABLE_TAB_SEARCH_INPUT_TEST_ID } from './test_ids';
@ -58,10 +55,10 @@ const search = {
* Retrieve the correct field from the BrowserField
*/
export const getFieldFromBrowserField = memoizeOne(
(field: string, browserFields: BrowserFields): BrowserField | undefined => {
(field: string, browserFields: BrowserFields): FieldSpec | undefined => {
const category = getCategory(field);
return browserFields[category]?.fields?.[field] as BrowserField;
return browserFields[category]?.fields?.[field] as FieldSpec;
},
(newArgs, lastArgs) => newArgs[0] === lastArgs[0]
);

View file

@ -96,7 +96,7 @@ export interface SelectedDataView {
/**
* @deprecated use EcsFlat or fields / indexFields from data view
*/
browserFields: SourcererDataView['browserFields'];
browserFields: BrowserFields;
dataViewId: string | null; // null if legacy pre-8.0 timeline
/**
* @deprecated use sourcererDataView

View file

@ -12,8 +12,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { isEmpty, isNumber } from 'lodash/fp';
import React from 'react';
import { css } from '@emotion/css';
import type { FieldSpec } from '@kbn/data-plugin/common';
import type { BrowserField } from '../../../../../common/containers/source';
import {
ALERT_HOST_CRITICALITY,
ALERT_USER_CRITICALITY,
@ -71,7 +71,7 @@ const FormattedFieldValueComponent: React.FC<{
isObjectArray?: boolean;
isUnifiedDataTable?: boolean;
fieldFormat?: string;
fieldFromBrowserField?: Partial<BrowserField>;
fieldFromBrowserField?: Partial<FieldSpec>;
fieldName: string;
fieldType?: string;
isButton?: boolean;

View file

@ -40,7 +40,6 @@ export { IS_OPERATOR, EXISTS_OPERATOR } from './types';
export type {
BeatFields,
BrowserField,
BrowserFields,
CursorType,
EqlOptionsData,

View file

@ -65,23 +65,10 @@ export interface IndexFieldsStrategyResponse extends IEsSearchResponse {
runtimeMappings: MappingRuntimeFields;
}
/**
* @deprecated use fields list on dataview / "indexPattern"
* about to use browserFields? Reconsider! Maybe you can accomplish
* everything you need via the `fields` property on the data view
* you are working with? Or perhaps you need a description for a
* particular field? Consider using the EcsFlat module from `@kbn/ecs`
*/
export type BrowserField = FieldSpec;
type FieldCategoryName = string;
export interface FieldCategory {
fields: Record<string, Partial<BrowserField>>;
}
export interface FieldCategory {
fields: Record<string, Partial<BrowserField>>;
fields: Record<string, Partial<FieldSpec>>;
}
/**