Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@

import React from 'react';
import { act } from 'react-dom/test-utils';
import { BehaviorSubject } from 'rxjs';
import { IUiSettingsClient } from '@kbn/core/public';
import { mountWithIntl as mount } from '@kbn/test-jest-helpers';
import { findTestSubject } from '@elastic/eui/lib/test';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { ESQLEditor } from './esql_editor';
import type { ESQLEditorProps } from './types';
import { ReactWrapper } from 'enzyme';
import { coreMock } from '@kbn/core/server/mocks';
import { coreMock } from '@kbn/core/public/mocks';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';

describe('ESQLEditor', () => {
Expand All @@ -25,12 +26,14 @@ describe('ESQLEditor', () => {
get: (key: string) => uiConfig[key],
} as IUiSettingsClient;

const corePluginMock = coreMock.createStart();
corePluginMock.chrome.getActiveSolutionNavId$.mockReturnValue(new BehaviorSubject('oblt'));
const services = {
uiSettings,
settings: {
client: uiSettings,
},
core: coreMock.createStart(),
core: corePluginMock,
data: dataPluginMock.createStartContract(),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { i18n } from '@kbn/i18n';
import moment from 'moment';
import { isEqual, memoize } from 'lodash';
import { CodeEditor, CodeEditorProps } from '@kbn/code-editor';
import useObservable from 'react-use/lib/useObservable';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import type { CoreStart } from '@kbn/core/public';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
Expand Down Expand Up @@ -126,6 +127,8 @@ export const ESQLEditor = memo(function ESQLEditor({
data,
} = kibana.services;

const activeSolutionId = useObservable(core.chrome.getActiveSolutionNavId$());

const fixedQuery = useMemo(
() => fixESQLQueryWithVariables(query.esql, esqlVariables),
[esqlVariables, query.esql]
Expand Down Expand Up @@ -512,15 +515,25 @@ export const ESQLEditor = memo(function ESQLEditor({
},
getJoinIndices: kibana.services?.esql?.getJoinIndicesAutocomplete,
getTimeseriesIndices: kibana.services?.esql?.getTimeseriesIndicesAutocomplete,
getEditorExtensions: async (queryString: string) => {
if (activeSolutionId) {
return (
(await kibana.services?.esql?.getEditorExtensionsAutocomplete(
queryString,
activeSolutionId
)) ?? []
);
}
return [];
},
};
return callbacks;
}, [
fieldsMetadata,
license,
kibana.services?.esql?.getJoinIndicesAutocomplete,
kibana.services?.esql?.getTimeseriesIndicesAutocomplete,
kibana.services?.esql,
dataSourcesCache,
fixedQuery,
license,
memoizedSources,
dataViews,
core,
Expand All @@ -529,10 +542,11 @@ export const ESQLEditor = memo(function ESQLEditor({
memoizedFieldsFromESQL,
expressions,
abortController,
indexManagementApiService,
histogramBarTarget,
variablesService?.esqlVariables,
variablesService?.areSuggestionsEnabled,
indexManagementApiService,
histogramBarTarget,
activeSolutionId,
]);

const queryRunButtonProperties = useMemo(() => {
Expand Down
10 changes: 9 additions & 1 deletion src/platform/packages/private/kbn-esql-editor/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import type { Storage } from '@kbn/kibana-utils-plugin/public';
import type { UiActionsStart } from '@kbn/ui-actions-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { ESQLControlVariable, IndicesAutocompleteResult } from '@kbn/esql-types';
import type {
ESQLControlVariable,
IndicesAutocompleteResult,
RecommendedQuery,
} from '@kbn/esql-types';

export interface ControlsContext {
/** The editor supports the creation of controls,
Expand Down Expand Up @@ -100,6 +104,10 @@ interface ESQLVariableService {
export interface EsqlPluginStartBase {
getJoinIndicesAutocomplete: () => Promise<IndicesAutocompleteResult>;
getTimeseriesIndicesAutocomplete: () => Promise<IndicesAutocompleteResult>;
getEditorExtensionsAutocomplete: (
queryString: string,
activeSolutionId: string
) => Promise<RecommendedQuery[]>;
variablesService: ESQLVariableService;
getLicense: () => Promise<ILicense | undefined>;
}
Expand Down
5 changes: 5 additions & 0 deletions src/platform/packages/shared/kbn-esql-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ export {
type IndicesAutocompleteResult,
type IndexAutocompleteItem,
} from './src/sources_autocomplete_types';

export {
type RecommendedQuery,
type ResolveIndexResponse,
} from './src/extensions_autocomplete_types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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 interface RecommendedQuery {
// The name of the recommended query, appears in the editor as a suggestion
name: string;
// The actual ESQL query string, this is what appears in the editor when the user selects the recommendation
query: string;
// Optional description of the query, can be used to provide more context, appears at the right side of the suggestion popover
description?: string;
}

interface ResolveIndexResponseItem {
name: string;
}

export interface ResolveIndexResponse {
indices?: ResolveIndexResponseItem[];
aliases?: ResolveIndexResponseItem[];
data_streams?: ResolveIndexResponseItem[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ export const timeseriesIndices: IndexAutocompleteItem[] = [
},
];

export const editorExtensions = [
{
name: 'Logs Count by Host',
query: 'from logs* | STATS count(*) by host',
},
];

export function getCallbackMocks(): ESQLCallbacks {
return {
getColumnsFor: jest.fn(async ({ query } = {}) => {
Expand Down Expand Up @@ -128,5 +135,11 @@ export function getCallbackMocks(): ESQLCallbacks {
getPolicies: jest.fn(async () => policies),
getJoinIndices: jest.fn(async () => ({ indices: joinIndices })),
getTimeseriesIndices: jest.fn(async () => ({ indices: timeseriesIndices })),
getEditorExtensions: jest.fn(async (queryString: string) => {
if (queryString.includes('logs*')) {
return editorExtensions;
}
return [];
}),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
FunctionReturnType,
SupportedDataType,
} from '../../definitions/types';
import { joinIndices, timeseriesIndices } from '../../__tests__/helpers';
import { joinIndices, timeseriesIndices, editorExtensions } from '../../__tests__/helpers';

export interface Integration {
name: string;
Expand Down Expand Up @@ -304,6 +304,12 @@ export function createCustomCallbackMocks(
getPolicies: jest.fn(async () => finalPolicies),
getJoinIndices: jest.fn(async () => ({ indices: joinIndices })),
getTimeseriesIndices: jest.fn(async () => ({ indices: timeseriesIndices })),
getEditorExtensions: jest.fn(async (queryString: string) => {
if (queryString.includes('logs*')) {
return editorExtensions;
}
return [];
}),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,17 @@ import {
TIME_PICKER_SUGGESTION,
} from './__tests__/helpers';
import { suggest } from './autocomplete';
import { editorExtensions } from '../__tests__/helpers';
import { getDateHistogramCompletionItem } from './commands/stats/util';
import { getSafeInsertText, TIME_SYSTEM_PARAMS, TRIGGER_SUGGESTION_COMMAND } from './factories';
import { getRecommendedQueries } from './recommended_queries/templates';
import { mapRecommendedQueriesFromExtensions } from './recommended_queries/suggestions';

const commandDefinitions = unmodifiedCommandDefinitions.filter(
({ name, hidden }) => !hidden && name !== 'rrf'
);

const getRecommendedQueriesSuggestions = (fromCommand: string, timeField?: string) =>
const getRecommendedQueriesSuggestionsFromTemplates = (fromCommand: string, timeField?: string) =>
getRecommendedQueries({
fromCommand,
timeField,
Expand Down Expand Up @@ -87,9 +89,13 @@ describe('autocomplete', () => {
const sourceCommands = ['row', 'from', 'show'];

describe('New command', () => {
const recommendedQuerySuggestions = getRecommendedQueriesSuggestions('FROM logs*', 'dateField');
const recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates(
'FROM logs*',
'dateField'
);
testSuggestions('/', [
...sourceCommands.map((name) => name.toUpperCase() + ' '),
...mapRecommendedQueriesFromExtensions(editorExtensions),
...recommendedQuerySuggestions.map((q) => q.queryString),
]);
const commands = commandDefinitions
Expand Down Expand Up @@ -243,9 +249,13 @@ describe('autocomplete', () => {
*/
describe('Invoke trigger kind (all commands)', () => {
// source command
let recommendedQuerySuggestions = getRecommendedQueriesSuggestions('FROM logs*', 'dateField');
let recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates(
'FROM logs*',
'dateField'
);
testSuggestions('f/', [
...sourceCommands.map((cmd) => `${cmd.toUpperCase()} `),
...mapRecommendedQueriesFromExtensions(editorExtensions),
...recommendedQuerySuggestions.map((q) => q.queryString),
]);

Expand Down Expand Up @@ -309,7 +319,7 @@ describe('autocomplete', () => {
]);

// FROM source METADATA
recommendedQuerySuggestions = getRecommendedQueriesSuggestions('', 'dateField');
recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates('', 'dateField');
testSuggestions('FROM index1 M/', ['METADATA ']);

// FROM source METADATA field
Expand Down Expand Up @@ -465,10 +475,14 @@ describe('autocomplete', () => {
...s,
asSnippet: true,
});
let recommendedQuerySuggestions = getRecommendedQueriesSuggestions('FROM logs*', 'dateField');
let recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates(
'FROM logs*',
'dateField'
);
// Source command
testSuggestions('F/', [
...['FROM ', 'ROW ', 'SHOW '].map(attachTriggerCommand),
...mapRecommendedQueriesFromExtensions(editorExtensions),
...recommendedQuerySuggestions.map((q) => q.queryString),
]);

Expand Down Expand Up @@ -555,7 +569,7 @@ describe('autocomplete', () => {
);
});

recommendedQuerySuggestions = getRecommendedQueriesSuggestions('', 'dateField');
recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates('', 'dateField');

// PIPE (|)
testSuggestions('FROM a /', [
Expand Down Expand Up @@ -604,7 +618,10 @@ describe('autocomplete', () => {
],
]
);
recommendedQuerySuggestions = getRecommendedQueriesSuggestions('index1', 'dateField');
recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates(
'index1',
'dateField'
);

testSuggestions(
'FROM index1/',
Expand All @@ -624,7 +641,10 @@ describe('autocomplete', () => {
]
);

recommendedQuerySuggestions = getRecommendedQueriesSuggestions('index2', 'dateField');
recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates(
'index2',
'dateField'
);
testSuggestions(
'FROM index1, index2/',
[
Expand All @@ -647,7 +667,10 @@ describe('autocomplete', () => {
// meaning that Monaco by default will only set the replacement
// range to cover "bar" and not "foo$bar". We have to make sure
// we're setting it ourselves.
recommendedQuerySuggestions = getRecommendedQueriesSuggestions('foo$bar', 'dateField');
recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates(
'foo$bar',
'dateField'
);
testSuggestions(
'FROM foo$bar/',
[
Expand Down Expand Up @@ -676,7 +699,10 @@ describe('autocomplete', () => {
);

// This is an identifier that matches multiple sources
recommendedQuerySuggestions = getRecommendedQueriesSuggestions('i*', 'dateField');
recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates(
'i*',
'dateField'
);
testSuggestions(
'FROM i*/',
[
Expand All @@ -696,7 +722,7 @@ describe('autocomplete', () => {
);
});

recommendedQuerySuggestions = getRecommendedQueriesSuggestions('', 'dateField');
recommendedQuerySuggestions = getRecommendedQueriesSuggestionsFromTemplates('', 'dateField');
// FROM source METADATA
testSuggestions('FROM index1 M/', [attachTriggerCommand('METADATA ')]);

Expand Down
Loading