diff --git a/src/platform/packages/shared/kbn-esql-types/index.ts b/src/platform/packages/shared/kbn-esql-types/index.ts index 3e5170652cf66..9fbe4ded1b0ff 100644 --- a/src/platform/packages/shared/kbn-esql-types/index.ts +++ b/src/platform/packages/shared/kbn-esql-types/index.ts @@ -34,5 +34,3 @@ export { type InferenceEndpointsAutocompleteResult, type InferenceEndpointAutocompleteItem, } from './src/inference_endpoint_autocomplete_types'; - -export { REGISTRY_EXTENSIONS_ROUTE } from './src/constants'; diff --git a/src/platform/packages/shared/kbn-esql-types/src/constants.ts b/src/platform/packages/shared/kbn-esql-types/src/constants.ts deleted file mode 100644 index ea361c19f1663..0000000000000 --- a/src/platform/packages/shared/kbn-esql-types/src/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -export const REGISTRY_EXTENSIONS_ROUTE = '/internal/esql_registry/extensions/'; diff --git a/src/platform/packages/shared/kbn-esql-validation-autocomplete/index.ts b/src/platform/packages/shared/kbn-esql-validation-autocomplete/index.ts index fa3474f4fce73..45807e954f52d 100644 --- a/src/platform/packages/shared/kbn-esql-validation-autocomplete/index.ts +++ b/src/platform/packages/shared/kbn-esql-validation-autocomplete/index.ts @@ -63,6 +63,4 @@ export { export { getRecommendedQueries } from './src/autocomplete/recommended_queries/templates'; -export { getRecommendedQueriesTemplatesFromExtensions } from './src/autocomplete/recommended_queries/suggestions'; - export { esqlFunctionNames } from './src/definitions/generated/function_names'; diff --git a/src/platform/plugins/shared/esql/public/plugin.ts b/src/platform/plugins/shared/esql/public/plugin.ts index 447c98eb1a2c2..6dd792e2c0984 100755 --- a/src/platform/plugins/shared/esql/public/plugin.ts +++ b/src/platform/plugins/shared/esql/public/plugin.ts @@ -16,7 +16,7 @@ import type { IndexManagementPluginSetup } from '@kbn/index-management-shared-ty import type { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public'; -import { type IndicesAutocompleteResult, REGISTRY_EXTENSIONS_ROUTE } from '@kbn/esql-types'; +import type { IndicesAutocompleteResult } from '@kbn/esql-types'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { KibanaProject as SolutionId } from '@kbn/projects-solutions-groups'; @@ -137,7 +137,7 @@ export class EsqlPlugin implements Plugin<{}, EsqlPluginStart> { activeSolutionId: SolutionId ) => { const result = await core.http.get( - `${REGISTRY_EXTENSIONS_ROUTE}${activeSolutionId}/${queryString}` + `/internal/esql_registry/extensions/${activeSolutionId}/${queryString}` ); return result; }; diff --git a/src/platform/plugins/shared/esql/server/plugin.ts b/src/platform/plugins/shared/esql/server/plugin.ts index 80a7a5683d89d..e6ba366cc051c 100644 --- a/src/platform/plugins/shared/esql/server/plugin.ts +++ b/src/platform/plugins/shared/esql/server/plugin.ts @@ -39,24 +39,6 @@ export class EsqlServerPlugin implements Plugin { }), }); - this.extensionsRegistry.setRecommendedQueries( - [ - { - name: 'Logs count by log level', - query: 'from logs* | STATS count(*) by log_level', - }, - { - name: 'Apache logs counts', - query: 'from logs-apache_error | STATS count(*)', - }, - { - name: 'Another index, not logs', - query: 'from movies | STATS count(*)', - }, - ], - 'oblt' - ); - registerRoutes(core, this.extensionsRegistry, initContext); return { diff --git a/src/platform/plugins/shared/esql/server/routes/get_esql_extensions_route.ts b/src/platform/plugins/shared/esql/server/routes/get_esql_extensions_route.ts index 64155dcaa863b..201ad6b4bce47 100644 --- a/src/platform/plugins/shared/esql/server/routes/get_esql_extensions_route.ts +++ b/src/platform/plugins/shared/esql/server/routes/get_esql_extensions_route.ts @@ -12,7 +12,7 @@ import { KIBANA_PROJECTS as VALID_SOLUTION_IDS, } from '@kbn/projects-solutions-groups'; import type { IRouter, PluginInitializerContext } from '@kbn/core/server'; -import { type ResolveIndexResponse, REGISTRY_EXTENSIONS_ROUTE } from '@kbn/esql-types'; +import type { ResolveIndexResponse } from '@kbn/esql-types'; import type { ESQLExtensionsRegistry } from '../extensions_registry'; /** @@ -39,7 +39,7 @@ export const registerESQLExtensionsRoute = ( ) => { router.get( { - path: `${REGISTRY_EXTENSIONS_ROUTE}{solutionId}/{query}`, + path: '/internal/esql_registry/extensions/{solutionId}/{query}', security: { authz: { enabled: false, diff --git a/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.test.tsx b/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.test.tsx index 38315e3504ca6..2a44f1957d266 100644 --- a/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.test.tsx +++ b/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.test.tsx @@ -8,8 +8,7 @@ */ import React from 'react'; -import { BehaviorSubject } from 'rxjs'; -import { screen, render, waitFor } from '@testing-library/react'; +import { screen, render } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { stubIndexPattern } from '@kbn/data-plugin/public/stubs'; @@ -17,22 +16,12 @@ import { coreMock } from '@kbn/core/public/mocks'; import type { DataView } from '@kbn/data-views-plugin/common'; import { ESQLMenuPopover } from './esql_menu_popover'; -const startMock = coreMock.createStart(); -// Mock the necessary services -startMock.chrome.getActiveSolutionNavId$.mockReturnValue(new BehaviorSubject('oblt')); -const httpModule = { - http: { - get: jest.fn().mockResolvedValue({ recommendedQueries: [] }), // Mock the HTTP GET request - }, -}; -const services = { - docLinks: startMock.docLinks, - http: httpModule.http, - chrome: startMock.chrome, -}; - describe('ESQLMenuPopover', () => { const renderESQLPopover = (adHocDataview?: DataView) => { + const startMock = coreMock.createStart(); + const services = { + docLinks: startMock.docLinks, + }; return render( @@ -40,11 +29,6 @@ describe('ESQLMenuPopover', () => { ); }; - beforeEach(() => { - // Reset mocks before each test - httpModule.http.get.mockClear(); - }); - it('should render a button', () => { renderESQLPopover(); expect(screen.getByTestId('esql-menu-button')).toBeInTheDocument(); @@ -65,52 +49,4 @@ describe('ESQLMenuPopover', () => { await userEvent.click(screen.getByRole('button')); expect(screen.queryByTestId('esql-recommended-queries')).toBeInTheDocument(); }); - - it('should fetch ESQL extensions when activeSolutionId and queryForRecommendedQueries are present and the popover is open', async () => { - const mockQueries = [ - { name: 'Count of logs', query: 'FROM logstash1 | STATS COUNT()' }, - { name: 'Average bytes', query: 'FROM logstash2 | STATS AVG(bytes) BY log.level' }, - ]; - - // Configure the mock to resolve with mockQueries - httpModule.http.get.mockResolvedValueOnce({ recommendedQueries: mockQueries }); - - renderESQLPopover(stubIndexPattern); - const esqlQuery = `FROM ${stubIndexPattern.name}`; - - // Assert that http.get was called with the correct URL - await waitFor(() => { - expect(httpModule.http.get).toHaveBeenCalledTimes(1); - expect(httpModule.http.get).toHaveBeenCalledWith( - `/internal/esql_registry/extensions/oblt/${esqlQuery}` - ); - }); - - // open the popover and check for recommended queries - await userEvent.click(screen.getByRole('button')); - expect(screen.queryByTestId('esql-recommended-queries')).toBeInTheDocument(); - // Open the nested section to see the recommended queries - await waitFor(() => userEvent.click(screen.getByTestId('esql-recommended-queries'))); - - await waitFor(() => { - expect(screen.getByText('Count of logs')).toBeInTheDocument(); - expect(screen.getByText('Average bytes')).toBeInTheDocument(); - }); - }); - - it('should handle API call failure gracefully', async () => { - // Configure the mock to reject with an error - httpModule.http.get.mockRejectedValueOnce(new Error('Network error')); - - renderESQLPopover(stubIndexPattern); - // Assert that http.get was called (even if it failed) - await waitFor(() => { - expect(httpModule.http.get).toHaveBeenCalledTimes(1); - }); - - // The catch block does nothing, so we assert that no error is thrown - // and that the static recommended queries are still shown. - await userEvent.click(screen.getByRole('button')); - expect(screen.queryByTestId('esql-recommended-queries')).toBeInTheDocument(); - }); }); diff --git a/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.tsx b/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.tsx index 4341856e7cefa..1b8226bb03cbf 100644 --- a/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.tsx +++ b/src/platform/plugins/shared/unified_search/public/query_string_input/esql_menu_popover.tsx @@ -6,27 +6,20 @@ * your election, the "Elastic License 2.0", the "GNU Affero General Public * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { useMemo, useState, useCallback, useEffect, useRef } from 'react'; + +import React, { useMemo, useState, useCallback } from 'react'; import { EuiPopover, EuiButton, type EuiContextMenuPanelDescriptor, EuiContextMenuItem, EuiContextMenu, - useEuiScrollBar, } from '@elastic/eui'; -import { isEqual } from 'lodash'; -import { css } from '@emotion/react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import useObservable from 'react-use/lib/useObservable'; import { i18n } from '@kbn/i18n'; import type { DataView } from '@kbn/data-views-plugin/public'; import { FEEDBACK_LINK } from '@kbn/esql-utils'; -import { type RecommendedQuery, REGISTRY_EXTENSIONS_ROUTE } from '@kbn/esql-types'; -import { - getRecommendedQueries, - getRecommendedQueriesTemplatesFromExtensions, -} from '@kbn/esql-validation-autocomplete'; +import { getRecommendedQueries } from '@kbn/esql-validation-autocomplete'; import { LanguageDocumentationFlyout } from '@kbn/language-documentation'; import type { IUnifiedSearchPluginServices } from '../types'; @@ -42,65 +35,12 @@ export const ESQLMenuPopover: React.FC = ({ onESQLQuerySubmit, }) => { const kibana = useKibana(); - const { docLinks, http, chrome } = kibana.services; - const activeSolutionId = useObservable(chrome.getActiveSolutionNavId$()); + const { docLinks } = kibana.services; const [isESQLMenuPopoverOpen, setIsESQLMenuPopoverOpen] = useState(false); const [isLanguageComponentOpen, setIsLanguageComponentOpen] = useState(false); - const [solutionsRecommendedQueries, setSolutionsRecommendedQueries] = useState< - RecommendedQuery[] - >([]); - - const { queryForRecommendedQueries, timeFieldName } = useMemo(() => { - if (adHocDataview && typeof adHocDataview !== 'string') { - return { - queryForRecommendedQueries: `FROM ${adHocDataview.name}`, - timeFieldName: - adHocDataview.timeFieldName ?? adHocDataview.fields?.getByType('date')?.[0]?.name, - }; - } - return { - queryForRecommendedQueries: '', - timeFieldName: undefined, - }; - }, [adHocDataview]); - - // Use a ref to store the *previous* fetched recommended queries - const lastFetchedQueries = useRef([]); - - useEffect(() => { - let cancelled = false; - const getESQLExtensions = async () => { - if (!activeSolutionId || !queryForRecommendedQueries) { - return; // Don't fetch if we don't have the active solution or query - } - - try { - const extensions: { recommendedQueries: RecommendedQuery[] } = await http.get( - `${REGISTRY_EXTENSIONS_ROUTE}${activeSolutionId}/${queryForRecommendedQueries}` - ); - - if (cancelled) return; - - // Only update state if the new data is actually different from the *last successfully set* data - if (!isEqual(extensions.recommendedQueries, lastFetchedQueries.current)) { - setSolutionsRecommendedQueries(extensions.recommendedQueries); - lastFetchedQueries.current = extensions.recommendedQueries; // Update the ref with the new data - } - } catch (error) { - // Do nothing if the extensions are not available - } - }; - - getESQLExtensions(); - - return () => { - cancelled = true; - }; - }, [activeSolutionId, http, queryForRecommendedQueries]); - - const toggleLanguageComponent = useCallback(() => { + const toggleLanguageComponent = useCallback(async () => { setIsLanguageComponentOpen(!isLanguageComponentOpen); setIsESQLMenuPopoverOpen(false); }, [isLanguageComponentOpen]); @@ -115,30 +55,18 @@ export const ESQLMenuPopover: React.FC = ({ const esqlContextMenuPanels = useMemo(() => { const recommendedQueries = []; - // If there are specific recommended queries for the current solution, process them. - if (solutionsRecommendedQueries.length) { - // Extract the core query templates by removing the 'FROM' clause. - const recommendedQueriesTemplatesFromExtensions = - getRecommendedQueriesTemplatesFromExtensions(solutionsRecommendedQueries); + if (adHocDataview && typeof adHocDataview !== 'string') { + const queryString = `FROM ${adHocDataview.name}`; + const timeFieldName = + adHocDataview.timeFieldName ?? adHocDataview.fields?.getByType('date')?.[0]?.name; - // Construct the full recommended queries by prepending the base 'FROM' command - // and add them to the main list of recommended queries. recommendedQueries.push( - ...recommendedQueriesTemplatesFromExtensions.map((template) => ({ - label: template.label, - queryString: `${queryForRecommendedQueries}${template.text}`, - })) + ...getRecommendedQueries({ + fromCommand: queryString, + timeField: timeFieldName, + }) ); } - // Handle the static recommended queries, no solutions specific - if (queryForRecommendedQueries && timeFieldName) { - const recommendedQueriesFromStaticTemplates = getRecommendedQueries({ - fromCommand: queryForRecommendedQueries, - timeField: timeFieldName, - }); - - recommendedQueries.push(...recommendedQueriesFromStaticTemplates); - } const panels = [ { id: 0, @@ -147,7 +75,7 @@ export const ESQLMenuPopover: React.FC = ({ name: i18n.translate('unifiedSearch.query.queryBar.esqlMenu.quickReference', { defaultMessage: 'Quick Reference', }), - icon: 'nedocumentationsted', // Typo: Should be 'documentation' + icon: 'nedocumentationsted', renderItem: () => ( = ({ }, ]; return panels as EuiContextMenuPanelDescriptor[]; - }, [ - docLinks.links.query.queryESQL, - onESQLQuerySubmit, - queryForRecommendedQueries, - timeFieldName, - toggleLanguageComponent, - solutionsRecommendedQueries, // This dependency is fine here, as it *uses* the state - ]); - - const esqlMenuPopoverStyles = css` - width: 240px; - max-height: 350px; - overflow-y: auto; - ${useEuiScrollBar()}; - `; + }, [adHocDataview, docLinks.links.query.queryESQL, onESQLQuerySubmit, toggleLanguageComponent]); return ( <> @@ -265,7 +179,7 @@ export const ESQLMenuPopover: React.FC = ({ } panelProps={{ ['data-test-subj']: 'esql-menu-popover', - css: esqlMenuPopoverStyles, + css: { width: 240 }, }} isOpen={isESQLMenuPopoverOpen} closePopover={() => setIsESQLMenuPopoverOpen(false)} diff --git a/src/platform/plugins/shared/unified_search/public/query_string_input/query_bar_top_row.test.tsx b/src/platform/plugins/shared/unified_search/public/query_string_input/query_bar_top_row.test.tsx index 9fefd129e6cfe..1863a8c3ac915 100644 --- a/src/platform/plugins/shared/unified_search/public/query_string_input/query_bar_top_row.test.tsx +++ b/src/platform/plugins/shared/unified_search/public/query_string_input/query_bar_top_row.test.tsx @@ -11,7 +11,6 @@ import { mockPersistedLogFactory } from './query_string_input.test.mocks'; import React from 'react'; import { mount, shallow } from 'enzyme'; -import { BehaviorSubject } from 'rxjs'; import { render } from '@testing-library/react'; import { EMPTY } from 'rxjs'; @@ -25,7 +24,6 @@ import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { unifiedSearchPluginMock } from '../mocks'; const startMock = coreMock.createStart(); -startMock.chrome.getActiveSolutionNavId$.mockReturnValue(new BehaviorSubject('oblt')); const mockTimeHistory = { get: () => { diff --git a/src/platform/plugins/shared/unified_search/public/search_bar/search_bar.test.tsx b/src/platform/plugins/shared/unified_search/public/search_bar/search_bar.test.tsx index f35388da324f7..869d139db5627 100644 --- a/src/platform/plugins/shared/unified_search/public/search_bar/search_bar.test.tsx +++ b/src/platform/plugins/shared/unified_search/public/search_bar/search_bar.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import SearchBar from './search_bar'; -import { BehaviorSubject } from 'rxjs'; + import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { indexPatternEditorPluginMock as dataViewEditorPluginMock } from '@kbn/data-view-editor-plugin/public/mocks'; import { I18nProvider } from '@kbn/i18n-react'; @@ -84,10 +84,6 @@ function wrapSearchBarInContext(testProps: any) { }, }, }, - chrome: { - ...startMock.chrome, - getActiveSolutionNavId$: jest.fn().mockReturnValue(new BehaviorSubject('oblt')), - }, uiSettings: startMock.uiSettings, settings: startMock.settings, savedObjects: startMock.savedObjects, diff --git a/src/platform/plugins/shared/unified_search/public/types.ts b/src/platform/plugins/shared/unified_search/public/types.ts index a058b06bbafd2..f9d0556447778 100755 --- a/src/platform/plugins/shared/unified_search/public/types.ts +++ b/src/platform/plugins/shared/unified_search/public/types.ts @@ -88,7 +88,6 @@ export interface IUnifiedSearchPluginServices extends Partial { autocomplete: AutocompleteStart; }; appName: string; - chrome: CoreStart['chrome']; uiSettings: CoreStart['uiSettings']; savedObjects: CoreStart['savedObjects']; notifications: CoreStart['notifications']; diff --git a/src/platform/plugins/shared/unified_search/tsconfig.json b/src/platform/plugins/shared/unified_search/tsconfig.json index 1f3354b512116..31284a5910538 100644 --- a/src/platform/plugins/shared/unified_search/tsconfig.json +++ b/src/platform/plugins/shared/unified_search/tsconfig.json @@ -48,8 +48,7 @@ "@kbn/esql-validation-autocomplete", "@kbn/react-kibana-mount", "@kbn/field-utils", - "@kbn/language-documentation", - "@kbn/esql-types" + "@kbn/language-documentation" ], "exclude": [ "target/**/*",