From 93fc80704ae7042107248cd7c1c6811c7e4b20e1 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 27 Sep 2023 19:08:16 +0300 Subject: [PATCH] [ES|QL] Do not allow saving in library action on text based panels (#167111) ## Summary Save to library action should not be present in ES|QL panels. For now we only allow by value embeddables and we don't want them to be edited/created in Lens editor image ### Checklist - [ ] [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 --- .../src/es_query/es_aggregate_query.test.ts | 5 +++++ .../kbn-es-query/src/es_query/es_aggregate_query.ts | 2 +- .../add_to_library_action.test.tsx | 13 +++++++++++++ .../dashboard_actions/add_to_library_action.tsx | 11 ++++++++--- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts b/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts index 2ab161e0f7517..34fc90805ca7d 100644 --- a/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts +++ b/packages/kbn-es-query/src/es_query/es_aggregate_query.test.ts @@ -33,6 +33,11 @@ describe('sql query helpers', () => { expect(flag).toBe(false); }); + it('should return false for an undefined query', () => { + const flag = isOfAggregateQueryType(undefined); + expect(flag).toBe(false); + }); + it('should return true for an Aggregate type query', () => { const flag = isOfAggregateQueryType({ sql: 'SELECT * FROM foo' }); expect(flag).toBe(true); diff --git a/packages/kbn-es-query/src/es_query/es_aggregate_query.ts b/packages/kbn-es-query/src/es_query/es_aggregate_query.ts index 1e87552e98b83..76f7113f2a589 100644 --- a/packages/kbn-es-query/src/es_query/es_aggregate_query.ts +++ b/packages/kbn-es-query/src/es_query/es_aggregate_query.ts @@ -18,7 +18,7 @@ export function isOfQueryType(arg?: Query | AggregateQuery): arg is Query { // currently only supports the sql query type // should be enhanced to support other query types export function isOfAggregateQueryType( - query: AggregateQuery | Query | { [key: string]: any } + query?: AggregateQuery | Query | { [key: string]: any } ): query is AggregateQuery { return Boolean(query && ('sql' in query || 'esql' in query)); } diff --git a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx index f7c1c2a30f68c..417795dee9334 100644 --- a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.test.tsx @@ -21,6 +21,7 @@ import { CONTACT_CARD_EMBEDDABLE, } from '@kbn/embeddable-plugin/public/lib/test_samples/embeddables'; import { embeddablePluginMock } from '@kbn/embeddable-plugin/public/mocks'; +import { type Query, type AggregateQuery, Filter } from '@kbn/es-query'; import { buildMockDashboard } from '../mocks'; import { pluginServices } from '../services/plugin_services'; @@ -82,6 +83,18 @@ test('Add to library is incompatible with Error Embeddables', async () => { expect(await action.isCompatible({ embeddable: errorEmbeddable })).toBe(false); }); +test('Add to library is incompatible with ES|QL Embeddables', async () => { + const action = new AddToLibraryAction(); + const mockGetFilters = jest.fn(async () => [] as Filter[]); + const mockGetQuery = jest.fn(async () => undefined as Query | AggregateQuery | undefined); + const filterableEmbeddable = embeddablePluginMock.mockFilterableEmbeddable(embeddable, { + getFilters: () => mockGetFilters(), + getQuery: () => mockGetQuery(), + }); + mockGetQuery.mockResolvedValue({ esql: 'from logstash-* | limit 10' } as AggregateQuery); + expect(await action.isCompatible({ embeddable: filterableEmbeddable })).toBe(false); +}); + test('Add to library is incompatible on visualize embeddable without visualize save permissions', async () => { pluginServices.getServices().application.capabilities = { ...defaultCapabilities, diff --git a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx index 3974c4f713efb..88e34f6028a5e 100644 --- a/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx +++ b/src/plugins/dashboard/public/dashboard_actions/add_to_library_action.tsx @@ -14,9 +14,10 @@ import { PanelNotFoundError, type EmbeddableInput, isReferenceOrValueEmbeddable, + isFilterableEmbeddable, } from '@kbn/embeddable-plugin/public'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; - +import { type AggregateQuery } from '@kbn/es-query'; import { DashboardPanelState } from '../../common'; import { pluginServices } from '../services/plugin_services'; import { dashboardAddToLibraryActionStrings } from './_dashboard_actions_strings'; @@ -61,7 +62,10 @@ export class AddToLibraryAction implements Action { // TODO: Fix this, potentially by adding a 'canSave' function to embeddable interface const { maps, visualize } = this.applicationCapabilities; const canSave = embeddable.type === 'map' ? maps.save : visualize.save; - + const { isOfAggregateQueryType } = await import('@kbn/es-query'); + const query = isFilterableEmbeddable(embeddable) && (await embeddable.getQuery()); + // Textbased panels (i.e. ES|QL, SQL) should not save to library + const isTextBasedEmbeddable = isOfAggregateQueryType(query as AggregateQuery); return Boolean( canSave && !isErrorEmbeddable(embeddable) && @@ -70,7 +74,8 @@ export class AddToLibraryAction implements Action { embeddable.getRoot().isContainer && embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE && isReferenceOrValueEmbeddable(embeddable) && - !embeddable.inputIsRefType(embeddable.getInput()) + !embeddable.inputIsRefType(embeddable.getInput()) && + !isTextBasedEmbeddable ); }