From cde74a6225b1c2cde22c466aacbf1979bb4037b6 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 19 Sep 2024 14:04:35 +0200 Subject: [PATCH 1/3] [ES|QL] Disable the filter actions for multivalue fields --- packages/kbn-esql-utils/src/utils/append_to_query.test.ts | 6 ++++++ packages/kbn-esql-utils/src/utils/append_to_query.ts | 6 +++++- .../application/main/components/layout/discover_layout.tsx | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/kbn-esql-utils/src/utils/append_to_query.test.ts b/packages/kbn-esql-utils/src/utils/append_to_query.test.ts index c9db52076222f..9dc15454cbbdf 100644 --- a/packages/kbn-esql-utils/src/utils/append_to_query.test.ts +++ b/packages/kbn-esql-utils/src/utils/append_to_query.test.ts @@ -168,5 +168,11 @@ AND \`dest\`=="Crete"` and \`ip\`::string!="127.0.0.2/32"` ); }); + + it('returns undefined for multivalue fields', () => { + expect( + appendWhereClauseToESQLQuery('from logstash-*', 'dest', ['meow'], '+', 'string') + ).toBeUndefined(); + }); }); }); diff --git a/packages/kbn-esql-utils/src/utils/append_to_query.ts b/packages/kbn-esql-utils/src/utils/append_to_query.ts index 76f317d55aa5d..26f9cb859d048 100644 --- a/packages/kbn-esql-utils/src/utils/append_to_query.ts +++ b/packages/kbn-esql-utils/src/utils/append_to_query.ts @@ -21,7 +21,11 @@ export function appendWhereClauseToESQLQuery( value: unknown, operation: '+' | '-' | 'is_not_null' | 'is_null', fieldType?: string -): string { +): string | undefined { + // multivalues filtering is not supported yet + if (Array.isArray(value)) { + return undefined; + } let operator; switch (operation) { case 'is_not_null': diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 82403ad38c710..49e645e3f2206 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -213,6 +213,9 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { getOperator(fieldName, values, operation), fieldType ); + if (!updatedQuery) { + return; + } data.query.queryString.setQuery({ esql: updatedQuery, }); From dd5d2230a69adbd8096b566a924289844630ec4d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 19 Sep 2024 14:59:07 +0000 Subject: [PATCH 2/3] [CI] Auto-commit changed files from 'yarn openapi:bundle' --- ...solution_entity_analytics_api_2023_10_31.bundled.schema.yaml | 2 +- ...solution_entity_analytics_api_2023_10_31.bundled.schema.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index 461d490305707..42e8db13d78a4 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -439,7 +439,7 @@ paths: - Security Solution Entity Analytics API /api/entity_store/entities/list: get: - description: 'List entities records, paging, sorting and filtering as needed.' + description: List entities records, paging, sorting and filtering as needed. operationId: ListEntities parameters: - in: query diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index b3584e7efd08c..ec3cf571b936e 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -439,7 +439,7 @@ paths: - Security Solution Entity Analytics API /api/entity_store/entities/list: get: - description: 'List entities records, paging, sorting and filtering as needed.' + description: List entities records, paging, sorting and filtering as needed. operationId: ListEntities parameters: - in: query From 776fba33493d83d778096ee0cf7f5c46cb5d4f24 Mon Sep 17 00:00:00 2001 From: Davis McPhee Date: Tue, 24 Sep 2024 00:31:51 -0300 Subject: [PATCH 3/3] Disable filtering for multivalue fields in ES|QL mode --- .../src/components/data_table_columns.tsx | 8 +- .../components/default_cell_actions.test.tsx | 83 +++++++++++++------ .../src/components/default_cell_actions.tsx | 66 ++++++++++----- 3 files changed, 112 insertions(+), 45 deletions(-) diff --git a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx index 4ef0636093f8d..13059f2881711 100644 --- a/packages/kbn-unified-data-table/src/components/data_table_columns.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table_columns.tsx @@ -182,7 +182,13 @@ function buildEuiGridColumn({ cellActions = columnCellActions; } else { cellActions = dataViewField - ? buildCellActions(dataViewField, toastNotifications, valueToStringConverter, onFilter) + ? buildCellActions( + dataViewField, + isPlainRecord, + toastNotifications, + valueToStringConverter, + onFilter + ) : []; if (columnCellActions?.length && cellActionsHandling === 'append') { diff --git a/packages/kbn-unified-data-table/src/components/default_cell_actions.test.tsx b/packages/kbn-unified-data-table/src/components/default_cell_actions.test.tsx index 628d3b5a29697..d097a2becd035 100644 --- a/packages/kbn-unified-data-table/src/components/default_cell_actions.test.tsx +++ b/packages/kbn-unified-data-table/src/components/default_cell_actions.test.tsx @@ -45,6 +45,7 @@ describe('Default cell actions ', function () { it('should not show cell actions for unfilterable fields', async () => { const cellActions = buildCellActions( { name: 'foo', filterable: false } as DataViewField, + false, servicesMock.toastNotifications, dataTableContextMock.valueToStringConverter ); @@ -61,6 +62,7 @@ describe('Default cell actions ', function () { it('should show filter actions for filterable fields', async () => { const cellActions = buildCellActions( { name: 'foo', filterable: true } as DataViewField, + false, servicesMock.toastNotifications, dataTableContextMock.valueToStringConverter, jest.fn() @@ -71,6 +73,7 @@ describe('Default cell actions ', function () { it('should show Copy action for _source field', async () => { const cellActions = buildCellActions( { name: '_source', type: '_source', filterable: false } as DataViewField, + false, servicesMock.toastNotifications, dataTableContextMock.valueToStringConverter ); @@ -87,65 +90,97 @@ describe('Default cell actions ', function () { const component = mountWithIntl( } - rowIndex={1} - colIndex={1} - columnId="extension" - isExpanded={false} + cellActionProps={{ + Component: (props: any) => , + rowIndex: 1, + colIndex: 1, + columnId: 'extension', + isExpanded: false, + }} + field={{ name: 'extension', filterable: true } as DataViewField} + isPlainRecord={false} /> ); const button = findTestSubject(component, 'filterForButton'); await button.simulate('click'); - expect(dataTableContextMock.onFilter).toHaveBeenCalledWith({}, 'jpg', '+'); + expect(dataTableContextMock.onFilter).toHaveBeenCalledWith( + { name: 'extension', filterable: true }, + 'jpg', + '+' + ); }); it('triggers filter function when FilterInBtn is clicked for a non-provided value', async () => { const component = mountWithIntl( } - rowIndex={0} - colIndex={1} - columnId="extension" - isExpanded={false} + cellActionProps={{ + Component: (props: any) => , + rowIndex: 0, + colIndex: 1, + columnId: 'extension', + isExpanded: false, + }} + field={{ name: 'extension', filterable: true } as DataViewField} + isPlainRecord={false} /> ); const button = findTestSubject(component, 'filterForButton'); await button.simulate('click'); - expect(dataTableContextMock.onFilter).toHaveBeenCalledWith({}, undefined, '+'); + expect(dataTableContextMock.onFilter).toHaveBeenCalledWith( + { name: 'extension', filterable: true }, + undefined, + '+' + ); }); it('triggers filter function when FilterInBtn is clicked for an empty string value', async () => { const component = mountWithIntl( } - rowIndex={4} - colIndex={1} - columnId="message" - isExpanded={false} + cellActionProps={{ + Component: (props: any) => , + rowIndex: 4, + colIndex: 1, + columnId: 'message', + isExpanded: false, + }} + field={{ name: 'message', filterable: true } as DataViewField} + isPlainRecord={false} /> ); const button = findTestSubject(component, 'filterForButton'); await button.simulate('click'); - expect(dataTableContextMock.onFilter).toHaveBeenCalledWith({}, '', '+'); + expect(dataTableContextMock.onFilter).toHaveBeenCalledWith( + { name: 'message', filterable: true }, + '', + '+' + ); }); it('triggers filter function when FilterOutBtn is clicked', async () => { const component = mountWithIntl( } - rowIndex={1} - colIndex={1} - columnId="extension" - isExpanded={false} + cellActionProps={{ + Component: (props: any) => , + rowIndex: 1, + colIndex: 1, + columnId: 'extension', + isExpanded: false, + }} + field={{ name: 'extension', filterable: true } as DataViewField} + isPlainRecord={false} /> ); const button = findTestSubject(component, 'filterOutButton'); await button.simulate('click'); - expect(dataTableContextMock.onFilter).toHaveBeenCalledWith({}, 'jpg', '-'); + expect(dataTableContextMock.onFilter).toHaveBeenCalledWith( + { name: 'extension', filterable: true }, + 'jpg', + '-' + ); }); it('triggers clipboard copy when CopyBtn is clicked', async () => { const component = mountWithIntl( diff --git a/packages/kbn-unified-data-table/src/components/default_cell_actions.tsx b/packages/kbn-unified-data-table/src/components/default_cell_actions.tsx index 620151c9d9701..ce7b354fc8dc1 100644 --- a/packages/kbn-unified-data-table/src/components/default_cell_actions.tsx +++ b/packages/kbn-unified-data-table/src/components/default_cell_actions.tsx @@ -32,11 +32,25 @@ function onFilterCell( } } -export const FilterInBtn = ( - { Component, rowIndex, columnId }: EuiDataGridColumnCellActionProps, - field: DataViewField -) => { +const esqlMultivalueFilteringDisabled = i18n.translate( + 'unifiedDataTable.grid.esqlMultivalueFilteringDisabled', + { + defaultMessage: 'Multivalue filtering is not supported in ES|QL', + } +); + +export const FilterInBtn = ({ + cellActionProps: { Component, rowIndex, columnId }, + field, + isPlainRecord, +}: { + cellActionProps: EuiDataGridColumnCellActionProps; + field: DataViewField; + isPlainRecord: boolean | undefined; +}) => { const context = useContext(UnifiedDataTableContext); + const filteringDisabled = + isPlainRecord && Array.isArray(context.rows[rowIndex]?.flattened[columnId]); const buttonTitle = i18n.translate('unifiedDataTable.grid.filterForAria', { defaultMessage: 'Filter for this {value}', values: { value: columnId }, @@ -49,7 +63,8 @@ export const FilterInBtn = ( }} iconType="plusInCircle" aria-label={buttonTitle} - title={buttonTitle} + title={filteringDisabled ? esqlMultivalueFilteringDisabled : buttonTitle} + disabled={filteringDisabled} data-test-subj="filterForButton" > {i18n.translate('unifiedDataTable.grid.filterFor', { @@ -59,11 +74,18 @@ export const FilterInBtn = ( ); }; -export const FilterOutBtn = ( - { Component, rowIndex, columnId }: EuiDataGridColumnCellActionProps, - field: DataViewField -) => { +export const FilterOutBtn = ({ + cellActionProps: { Component, rowIndex, columnId }, + field, + isPlainRecord, +}: { + cellActionProps: EuiDataGridColumnCellActionProps; + field: DataViewField; + isPlainRecord: boolean | undefined; +}) => { const context = useContext(UnifiedDataTableContext); + const filteringDisabled = + isPlainRecord && Array.isArray(context.rows[rowIndex]?.flattened[columnId]); const buttonTitle = i18n.translate('unifiedDataTable.grid.filterOutAria', { defaultMessage: 'Filter out this {value}', values: { value: columnId }, @@ -76,7 +98,8 @@ export const FilterOutBtn = ( }} iconType="minusInCircle" aria-label={buttonTitle} - title={buttonTitle} + title={filteringDisabled ? esqlMultivalueFilteringDisabled : buttonTitle} + disabled={filteringDisabled} data-test-subj="filterOutButton" > {i18n.translate('unifiedDataTable.grid.filterOut', { @@ -120,6 +143,7 @@ export function buildCopyValueButton( export function buildCellActions( field: DataViewField, + isPlainRecord: boolean | undefined, toastNotifications: ToastsStart, valueToStringConverter: ValueToStringConverter, onFilter?: DocViewFilterFn @@ -127,16 +151,18 @@ export function buildCellActions( return [ ...(onFilter && field.filterable ? [ - ({ Component, rowIndex, columnId }: EuiDataGridColumnCellActionProps) => - FilterInBtn( - { Component, rowIndex, columnId } as EuiDataGridColumnCellActionProps, - field - ), - ({ Component, rowIndex, columnId }: EuiDataGridColumnCellActionProps) => - FilterOutBtn( - { Component, rowIndex, columnId } as EuiDataGridColumnCellActionProps, - field - ), + (cellActionProps: EuiDataGridColumnCellActionProps) => + FilterInBtn({ + cellActionProps, + field, + isPlainRecord, + }), + (cellActionProps: EuiDataGridColumnCellActionProps) => + FilterOutBtn({ + cellActionProps, + field, + isPlainRecord, + }), ] : []), ({ Component, rowIndex, columnId }: EuiDataGridColumnCellActionProps) =>