From edf5c762d8ad0057e4872ba10e92c0deb34a7003 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Thu, 8 Aug 2024 07:11:43 -0600 Subject: [PATCH] [ES|QL] Stronger typing for ESQL field interface (#189941) ## Summary The heart of this PR is [this change](https://github.com/elastic/kibana/pull/189941/files#diff-88513481c44d7b2de70ca2f7826c2b1fb1d8bda5b308aab0f8917a42ac2c24b7R11-R94) where I clean up and clarify the various data-type-related types floating around in the engines + [this change](https://github.com/elastic/kibana/pull/189941/files#diff-f48b526b82119bd591cf781262173d7a0233d236ab26496a4c06f5ea9a441561R21) where I add strong typing to the ES|QL field interface. Pretty much everything else is a result of that. For example, strongly typing the fields and test helpers highlighted a bunch of tests that were still using Kibana types instead of Elasticsearch types. So, then those had to be updated. There's more work to do to extend the strong field typing to the rest of the engines, but this got big and I decided to do it piece-meal. Next plans - Extend typing to subroutines in autocomplete and validation engines (e.g. the stuff in `factories.ts`) - Add typing to the [variable interface](https://github.com/elastic/kibana/pull/189941/files#diff-f48b526b82119bd591cf781262173d7a0233d236ab26496a4c06f5ea9a441561R13) - Consider merging `time_literal` and `time_duration` types - "It looks like timespan literals are the way to write a constant time_duration. and time_durations can only be constants at the moment. so they aren't the same, but sure are about the same" - Nik - Consider merging `packages/kbn-esql-validation-autocomplete/src/shared/esql_types.ts` with `packages/kbn-esql-validation-autocomplete/src/definitions/types.ts` in some common place ### Checklist - [x] [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 --- .../esql_validation_example/public/app.tsx | 11 +- .../generate_function_validation_tests.ts | 45 +- .../src/__tests__/helpers.ts | 18 +- .../autocomplete.command.stats.test.ts | 32 +- .../src/autocomplete/__tests__/helpers.ts | 39 +- .../src/autocomplete/autocomplete.test.ts | 346 +++---- .../src/autocomplete/autocomplete.ts | 34 +- .../src/autocomplete/complete_items.ts | 5 +- .../src/autocomplete/factories.ts | 3 +- .../src/code_actions/actions.test.ts | 113 +-- .../src/definitions/builtin.ts | 8 +- .../src/definitions/helpers.ts | 4 +- .../src/definitions/types.ts | 111 ++- .../src/shared/helpers.ts | 5 +- .../src/shared/variables.ts | 2 +- .../esql_validation_meta_tests.json | 882 +++++++----------- .../src/validation/types.ts | 4 +- .../src/validation/validation.test.ts | 101 +- .../src/validation/validation.ts | 38 +- .../src/esql/lib/hover/hover.test.ts | 14 +- .../src/ecs_metadata_helper.test.ts | 26 +- .../src/text_based_languages_editor.tsx | 13 +- test/api_integration/apis/esql/errors.ts | 7 +- 23 files changed, 811 insertions(+), 1050 deletions(-) diff --git a/examples/esql_validation_example/public/app.tsx b/examples/esql_validation_example/public/app.tsx index f785b31de3ba2..62b6405678d15 100644 --- a/examples/esql_validation_example/public/app.tsx +++ b/examples/esql_validation_example/public/app.tsx @@ -23,7 +23,7 @@ import { import type { CoreStart } from '@kbn/core/public'; -import { ESQLCallbacks, validateQuery } from '@kbn/esql-validation-autocomplete'; +import { ESQLCallbacks, ESQLRealField, validateQuery } from '@kbn/esql-validation-autocomplete'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; import type { StartDependencies } from './plugin'; import { CodeSnippet } from './code_snippet'; @@ -52,10 +52,11 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => { ['index1', 'anotherIndex', 'dataStream'].map((name) => ({ name, hidden: false })) : undefined, getFieldsFor: callbacksEnabled.fields - ? async () => [ - { name: 'numberField', type: 'number' }, - { name: 'stringField', type: 'string' }, - ] + ? async () => + [ + { name: 'doubleField', type: 'double' }, + { name: 'keywordField', type: 'keyword' }, + ] as ESQLRealField[] : undefined, getPolicies: callbacksEnabled.policies ? async () => [ diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts index d3497115aecf9..f431d3cddb2c5 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts @@ -18,16 +18,17 @@ import { getFunctionSignatures } from '../src/definitions/helpers'; import { timeUnits } from '../src/definitions/literals'; import { nonNullable } from '../src/shared/helpers'; import { - SupportedFieldType, + SupportedDataType, FunctionDefinition, - supportedFieldTypes, - isSupportedFieldType, + dataTypes, + isSupportedDataType, + fieldTypes, } from '../src/definitions/types'; import { FUNCTION_DESCRIBE_BLOCK_NAME } from '../src/validation/function_describe_block_name'; import { getMaxMinNumberOfParams } from '../src/validation/helpers'; import { ESQL_NUMBER_TYPES, isNumericType, isStringType } from '../src/shared/esql_types'; -export const fieldNameFromType = (type: SupportedFieldType) => `${camelCase(type)}Field`; +export const fieldNameFromType = (type: SupportedDataType) => `${camelCase(type)}Field`; function main() { const testCasesByFunction: Map> = new Map(); @@ -301,8 +302,8 @@ function generateWhereCommandTestsForEvalFunction( // TODO: not sure why there's this constraint... const supportedFunction = signatures.some( ({ returnType, params }) => - [...ESQL_NUMBER_TYPES, 'string'].includes(returnType) && - params.every(({ type }) => [...ESQL_NUMBER_TYPES, 'string'].includes(type)) + [...ESQL_NUMBER_TYPES, 'string'].includes(returnType as string) && + params.every(({ type }) => [...ESQL_NUMBER_TYPES, 'string'].includes(type as string)) ); if (!supportedFunction) { @@ -312,7 +313,7 @@ function generateWhereCommandTestsForEvalFunction( const supportedSignatures = signatures.filter(({ returnType }) => // TODO — not sure why the tests have this limitation... seems like any type // that can be part of a boolean expression should be allowed in a where clause - [...ESQL_NUMBER_TYPES, 'string'].includes(returnType) + [...ESQL_NUMBER_TYPES, 'string'].includes(returnType as string) ); for (const { params, returnType, ...restSign } of supportedSignatures) { const correctMapping = getFieldMapping(params); @@ -905,7 +906,7 @@ function generateStatsCommandTestsForGroupingFunction( fieldReplacedType // if a param of type time_literal or chrono_literal it will always be a literal // so no way to test the constantOnly thing - .filter((type) => !['time_literal', 'chrono_literal'].includes(type)) + .filter((type) => !['time_literal'].includes(type as string)) .map((type) => `Argument of [${name}] must be a constant, received [${type}Field]`) ); } @@ -965,7 +966,7 @@ function generateSortCommandTestsForAggFunction( const generateSortCommandTestsForGroupingFunction = generateSortCommandTestsForAggFunction; -const fieldTypesToConstants: Record = { +const fieldTypesToConstants: Record = { text: '"a"', keyword: '"a"', double: '5.5', @@ -984,14 +985,20 @@ const fieldTypesToConstants: Record = { geo_shape: 'to_geoshape("POINT (30 10)")', cartesian_point: 'to_cartesianpoint("POINT (30 10)")', cartesian_shape: 'to_cartesianshape("POINT (30 10)")', + null: 'NULL', + time_duration: '1 day', + // the following are never supplied + // by the ES function definitions. Just making types happy + time_literal: '1 day', + unsupported: '', }; -const supportedTypesAndFieldNames = supportedFieldTypes.map((type) => ({ +const supportedTypesAndFieldNames = fieldTypes.map((type) => ({ name: fieldNameFromType(type), type, })); -const supportedTypesAndConstants = supportedFieldTypes.map((type) => ({ +const supportedTypesAndConstants = dataTypes.map((type) => ({ name: fieldTypesToConstants[type], type, })); @@ -1029,7 +1036,7 @@ const toCartesianShapeSignature = evalFunctionDefinitions.find( const toVersionSignature = evalFunctionDefinitions.find(({ name }) => name === 'to_version')!; // We don't have full list for long, unsigned_long, etc. -const nestedFunctions: Record = { +const nestedFunctions: Record = { double: prepareNestedFunction(toDoubleSignature), integer: prepareNestedFunction(toInteger), text: prepareNestedFunction(toStringSignature), @@ -1046,7 +1053,7 @@ const nestedFunctions: Record = { }; function getFieldName( - typeString: SupportedFieldType, + typeString: SupportedDataType, { useNestedFunction, isStats }: { useNestedFunction: boolean; isStats: boolean } ) { if (useNestedFunction && isStats) { @@ -1082,7 +1089,7 @@ function tweakSignatureForRowCommand(signature: string): string { */ let ret = signature; for (const [type, value] of Object.entries(fieldTypesToConstants)) { - ret = ret.replace(new RegExp(fieldNameFromType(type as SupportedFieldType), 'g'), value); + ret = ret.replace(new RegExp(fieldNameFromType(type as SupportedDataType), 'g'), value); } return ret; } @@ -1101,8 +1108,8 @@ function getFieldMapping( }; return params.map(({ name: _name, type, constantOnly, literalOptions, ...rest }) => { - const typeString: string = type; - if (isSupportedFieldType(typeString)) { + const typeString: string = type as string; + if (isSupportedDataType(typeString)) { if (useLiterals && literalOptions) { return { name: `"${literalOptions[0]}"`, @@ -1146,7 +1153,7 @@ function generateIncorrectlyTypedParameters( name: string, signatures: FunctionDefinition['signatures'], currentParams: FunctionDefinition['signatures'][number]['params'], - availableFields: Array<{ name: string; type: SupportedFieldType }> + availableFields: Array<{ name: string; type: SupportedDataType }> ) { const literalValues = { string: `"a"`, @@ -1167,7 +1174,7 @@ function generateIncorrectlyTypedParameters( if (type !== 'any') { // try to find an unacceptable field - const unacceptableField: { name: string; type: SupportedFieldType } | undefined = + const unacceptableField: { name: string; type: SupportedDataType } | undefined = availableFields // sort to make the test deterministic .sort((a, b) => a.type.localeCompare(b.type)) @@ -1187,7 +1194,7 @@ function generateIncorrectlyTypedParameters( } // failed to find a bad field... they must all be acceptable - const acceptableField: { name: string; type: SupportedFieldType } | undefined = + const acceptableField: { name: string; type: SupportedDataType } | undefined = type === 'any' ? availableFields[0] : availableFields.find(({ type: fieldType }) => fieldType === type); diff --git a/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts index 5f24d86e718bc..273679ab1f267 100644 --- a/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/__tests__/helpers.ts @@ -7,22 +7,27 @@ */ import { camelCase } from 'lodash'; -import { supportedFieldTypes } from '../definitions/types'; +import { ESQLRealField } from '../validation/types'; +import { fieldTypes } from '../definitions/types'; -export const fields = [ - ...supportedFieldTypes.map((type) => ({ name: `${camelCase(type)}Field`, type })), +export const fields: ESQLRealField[] = [ + ...fieldTypes + .map((type) => ({ name: `${camelCase(type)}Field`, type })) + .filter((f) => f.type !== 'unsupported'), { name: 'any#Char$Field', type: 'double' }, { name: 'kubernetes.something.something', type: 'double' }, { name: '@timestamp', type: 'date' }, ]; -export const enrichFields = [ +export const enrichFields: ESQLRealField[] = [ { name: 'otherField', type: 'text' }, { name: 'yetAnotherField', type: 'double' }, ]; // eslint-disable-next-line @typescript-eslint/naming-convention -export const unsupported_field = [{ name: 'unsupported_field', type: 'unsupported' }]; +export const unsupported_field: ESQLRealField[] = [ + { name: 'unsupported_field', type: 'unsupported' }, +]; export const indexes = [ 'a_index', @@ -58,7 +63,8 @@ export function getCallbackMocks() { return unsupported_field; } if (/dissect|grok/.test(query)) { - return [{ name: 'firstWord', type: 'text' }]; + const field: ESQLRealField = { name: 'firstWord', type: 'text' }; + return [field]; } return fields; }), diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts index a94fb0b8bead9..cdf4a6239a9ab 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/autocomplete.command.stats.test.ts @@ -9,8 +9,6 @@ import { ESQL_COMMON_NUMERIC_TYPES, ESQL_NUMBER_TYPES } from '../../shared/esql_types'; import { setup, getFunctionSignaturesByReturnType, getFieldNamesByType } from './helpers'; -const ESQL_NUMERIC_TYPES = ESQL_NUMBER_TYPES as unknown as string[]; - const allAggFunctions = getFunctionSignaturesByReturnType('stats', 'any', { agg: true, }); @@ -84,51 +82,51 @@ describe('autocomplete.suggest', () => { scalar: true, }).map((s) => ({ ...s, text: `${s.text},` })), ]); - + const roundParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; await assertSuggestions('from a | stats round(/', [ - ...getFunctionSignaturesByReturnType('stats', ESQL_NUMERIC_TYPES, { + ...getFunctionSignaturesByReturnType('stats', roundParameterTypes, { agg: true, grouping: true, }), - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] ), ]); await assertSuggestions('from a | stats round(round(/', [ - ...getFunctionSignaturesByReturnType('stats', ESQL_NUMERIC_TYPES, { agg: true }), - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('stats', roundParameterTypes, { agg: true }), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + ESQL_NUMBER_TYPES, { scalar: true }, undefined, ['round'] ), ]); await assertSuggestions('from a | stats avg(round(/', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + ESQL_NUMBER_TYPES, { scalar: true }, undefined, ['round'] ), ]); await assertSuggestions('from a | stats avg(/', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_NUMBER_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_NUMBER_TYPES, { scalar: true }), ]); await assertSuggestions('from a | stats round(avg(/', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(ESQL_NUMBER_TYPES), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + ESQL_NUMBER_TYPES, { scalar: true }, undefined, ['round'] @@ -142,7 +140,7 @@ describe('autocomplete.suggest', () => { ...getFieldNamesByType([...ESQL_COMMON_NUMERIC_TYPES, 'date', 'boolean', 'ip']), ...getFunctionSignaturesByReturnType( 'stats', - [...ESQL_COMMON_NUMERIC_TYPES, 'date', 'boolean', 'ip'], + [...ESQL_COMMON_NUMERIC_TYPES, 'date', 'date_period', 'boolean', 'ip'], { scalar: true, } @@ -158,7 +156,7 @@ describe('autocomplete.suggest', () => { const { assertSuggestions } = await setup(); await assertSuggestions('from a | stats avg(b/) by stringField', [ - ...getFieldNamesByType('double'), + ...getFieldNamesByType(ESQL_NUMBER_TYPES), ...getFunctionSignaturesByReturnType( 'eval', ['double', 'integer', 'long', 'unsigned_long'], diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts index 464239d3ae960..4c4ad9902ac8c 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/__tests__/helpers.ts @@ -18,6 +18,14 @@ import type { ESQLCallbacks } from '../../shared/types'; import type { EditorContext, SuggestionRawDefinition } from '../types'; import { TIME_SYSTEM_PARAMS } from '../factories'; import { getFunctionSignatures } from '../../definitions/helpers'; +import { ESQLRealField } from '../../validation/types'; +import { + FieldType, + fieldTypes, + FunctionParameterType, + FunctionReturnType, + SupportedDataType, +} from '../../definitions/types'; export interface Integration { name: string; @@ -38,19 +46,8 @@ export const TIME_PICKER_SUGGESTION: PartialSuggestionWithText = { export const triggerCharacters = [',', '(', '=', ' ']; -export const fields: Array<{ name: string; type: string; suggestedAs?: string }> = [ - ...[ - 'string', - 'keyword', - 'double', - 'date', - 'boolean', - 'ip', - 'geo_point', - 'geo_shape', - 'cartesian_point', - 'cartesian_shape', - ].map((type) => ({ +export const fields: Array = [ + ...fieldTypes.map((type) => ({ name: `${camelCase(type)}Field`, type, })), @@ -125,7 +122,7 @@ export const policies = [ */ export function getFunctionSignaturesByReturnType( command: string, - _expectedReturnType: string | string[], + _expectedReturnType: Readonly>, { agg, grouping, @@ -141,7 +138,7 @@ export function getFunctionSignaturesByReturnType( builtin?: boolean; skipAssign?: boolean; } = {}, - paramsTypes?: string[], + paramsTypes?: Readonly, ignored?: string[], option?: string ): PartialSuggestionWithText[] { @@ -178,7 +175,7 @@ export function getFunctionSignaturesByReturnType( } const filteredByReturnType = signatures.filter( ({ returnType }) => - expectedReturnType.includes('any') || expectedReturnType.includes(returnType) + expectedReturnType.includes('any') || expectedReturnType.includes(returnType as string) ); if (!filteredByReturnType.length) { return false; @@ -227,14 +224,16 @@ export function getFunctionSignaturesByReturnType( }); } -export function getFieldNamesByType(_requestedType: string | string[]) { +export function getFieldNamesByType( + _requestedType: Readonly> +) { const requestedType = Array.isArray(_requestedType) ? _requestedType : [_requestedType]; return fields .filter(({ type }) => requestedType.includes('any') || requestedType.includes(type)) .map(({ name, suggestedAs }) => suggestedAs || name); } -export function getLiteralsByType(_type: string | string[]) { +export function getLiteralsByType(_type: SupportedDataType | SupportedDataType[]) { const type = Array.isArray(_type) ? _type : [_type]; if (type.includes('time_literal')) { // return only singular @@ -243,13 +242,13 @@ export function getLiteralsByType(_type: string | string[]) { return []; } -export function getDateLiteralsByFieldType(_requestedType: string | string[]) { +export function getDateLiteralsByFieldType(_requestedType: FieldType | FieldType[]) { const requestedType = Array.isArray(_requestedType) ? _requestedType : [_requestedType]; return requestedType.includes('date') ? [TIME_PICKER_SUGGESTION, ...TIME_SYSTEM_PARAMS] : []; } export function createCustomCallbackMocks( - customFields?: Array<{ name: string; type: string }>, + customFields?: ESQLRealField[], customSources?: Array<{ name: string; hidden: boolean }>, customPolicies?: Array<{ name: string; diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts index a6c2756914ce2..c816b49e66f78 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts @@ -18,7 +18,12 @@ import { } from './factories'; import { camelCase, partition } from 'lodash'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; -import { FunctionParameter, FunctionReturnType } from '../definitions/types'; +import { + FunctionParameter, + isFieldType, + isSupportedDataType, + SupportedDataType, +} from '../definitions/types'; import { getParamAtPosition } from './helper'; import { nonNullable } from '../shared/helpers'; import { @@ -34,14 +39,11 @@ import { TIME_PICKER_SUGGESTION, } from './__tests__/helpers'; import { METADATA_FIELDS } from '../shared/constants'; -import { - ESQL_COMMON_NUMERIC_TYPES as UNCASTED_ESQL_COMMON_NUMERIC_TYPES, - ESQL_NUMBER_TYPES, -} from '../shared/esql_types'; +import { ESQL_COMMON_NUMERIC_TYPES, ESQL_STRING_TYPES } from '../shared/esql_types'; -const ESQL_NUMERIC_TYPES = ESQL_NUMBER_TYPES as unknown as string[]; -const ESQL_COMMON_NUMERIC_TYPES = - UNCASTED_ESQL_COMMON_NUMERIC_TYPES as unknown as FunctionReturnType[]; +const roundParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; +const powParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; +const log10ParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; describe('autocomplete', () => { type TestArgs = [ @@ -166,60 +168,42 @@ describe('autocomplete', () => { 'var0', ...allEvalFns, ]); - testSuggestions('from a | where stringField ', [ - // all functions compatible with a stringField type - ...getFunctionSignaturesByReturnType( - 'where', - 'boolean', - { - builtin: true, - }, - ['string'] - ), - ]); - testSuggestions('from a | where textField >= ', [ - ...getFieldNamesByType('any'), - ...getFunctionSignaturesByReturnType('where', ['any'], { scalar: true }), - ]); - - testSuggestions('from a | where dateField >= ', [ - TIME_PICKER_SUGGESTION, - ...TIME_SYSTEM_PARAMS, - ...getFieldNamesByType('date'), - ...getFunctionSignaturesByReturnType('where', ['date'], { scalar: true }), - ]); - - // Skip these tests until the insensitive case equality gets restored back - testSuggestions.skip('from a | where stringField =~ ', [ - ...getFieldNamesByType('string'), - ...getFunctionSignaturesByReturnType('where', 'string', { scalar: true }), - ]); - testSuggestions('from a | where textField >= textField', [ - ...getFieldNamesByType('any'), - ...getFunctionSignaturesByReturnType('where', 'any', { scalar: true }), - ]); - testSuggestions.skip('from a | where stringField =~ stringField ', [ - '| ', + testSuggestions('from a | where keywordField ', [ + // all functions compatible with a keywordField type ...getFunctionSignaturesByReturnType( 'where', 'boolean', { builtin: true, }, - ['boolean'] + undefined, + ['and', 'or', 'not'] ), ]); + const expectedComparisonWithTextFieldSuggestions = [ + ...getFieldNamesByType(['text', 'keyword', 'ip', 'version']), + ...getFunctionSignaturesByReturnType('where', ['text', 'keyword', 'ip', 'version'], { + scalar: true, + }), + ]; + testSuggestions('from a | where textField >= ', expectedComparisonWithTextFieldSuggestions); + testSuggestions( + 'from a | where textField >= textField', + expectedComparisonWithTextFieldSuggestions + ); for (const op of ['and', 'or']) { - testSuggestions(`from a | where stringField >= stringField ${op} `, [ + testSuggestions(`from a | where keywordField >= keywordField ${op} `, [ ...getFieldNamesByType('any'), ...getFunctionSignaturesByReturnType('where', 'any', { scalar: true }), ]); - testSuggestions(`from a | where stringField >= stringField ${op} doubleField `, [ + testSuggestions(`from a | where keywordField >= keywordField ${op} doubleField `, [ ...getFunctionSignaturesByReturnType('where', 'boolean', { builtin: true }, ['double']), ]); - testSuggestions(`from a | where stringField >= stringField ${op} doubleField == `, [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('where', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + testSuggestions(`from a | where keywordField >= keywordField ${op} doubleField == `, [ + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('where', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); } testSuggestions('from a | stats a=avg(doubleField) | where a ', [ @@ -242,10 +226,10 @@ describe('autocomplete', () => { testSuggestions( 'from a | where log10()', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(log10ParameterTypes), ...getFunctionSignaturesByReturnType( 'where', - ESQL_NUMERIC_TYPES, + log10ParameterTypes, { scalar: true }, undefined, ['log10'] @@ -260,10 +244,10 @@ describe('autocomplete', () => { testSuggestions( 'from a | WHERE pow(doubleField, )', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(powParameterTypes), ...getFunctionSignaturesByReturnType( 'where', - ESQL_NUMERIC_TYPES, + powParameterTypes, { scalar: true }, undefined, ['pow'] @@ -272,8 +256,8 @@ describe('autocomplete', () => { ',' ); - testSuggestions('from index | WHERE stringField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); - testSuggestions('from index | WHERE stringField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | WHERE keywordField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | WHERE keywordField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); testSuggestions('from index | WHERE not ', [ ...getFieldNamesByType('boolean'), ...getFunctionSignaturesByReturnType('eval', 'boolean', { scalar: true }), @@ -316,15 +300,19 @@ describe('autocomplete', () => { const constantPattern = '"%{WORD:firstWord}"'; const subExpressions = [ '', - `grok stringField |`, - `grok stringField ${constantPattern} |`, - `dissect stringField ${constantPattern} append_separator = ":" |`, - `dissect stringField ${constantPattern} |`, + `grok keywordField |`, + `grok keywordField ${constantPattern} |`, + `dissect keywordField ${constantPattern} append_separator = ":" |`, + `dissect keywordField ${constantPattern} |`, ]; for (const subExpression of subExpressions) { - testSuggestions(`from a | ${subExpression} grok `, getFieldNamesByType('string')); - testSuggestions(`from a | ${subExpression} grok stringField `, [constantPattern], ' '); - testSuggestions(`from a | ${subExpression} grok stringField ${constantPattern} `, ['| ']); + // Unskip once https://github.com/elastic/kibana/issues/190070 is fixed + testSuggestions.skip( + `from a | ${subExpression} grok `, + getFieldNamesByType(ESQL_STRING_TYPES) + ); + testSuggestions(`from a | ${subExpression} grok keywordField `, [constantPattern], ' '); + testSuggestions(`from a | ${subExpression} grok keywordField ${constantPattern} `, ['| ']); } }); @@ -332,24 +320,28 @@ describe('autocomplete', () => { const constantPattern = '"%{firstWord}"'; const subExpressions = [ '', - `dissect stringField |`, - `dissect stringField ${constantPattern} |`, - `dissect stringField ${constantPattern} append_separator = ":" |`, + `dissect keywordField |`, + `dissect keywordField ${constantPattern} |`, + `dissect keywordField ${constantPattern} append_separator = ":" |`, ]; for (const subExpression of subExpressions) { - testSuggestions(`from a | ${subExpression} dissect `, getFieldNamesByType('string')); - testSuggestions(`from a | ${subExpression} dissect stringField `, [constantPattern], ' '); + // Unskip once https://github.com/elastic/kibana/issues/190070 is fixed + testSuggestions.skip( + `from a | ${subExpression} dissect `, + getFieldNamesByType(ESQL_STRING_TYPES) + ); + testSuggestions(`from a | ${subExpression} dissect keywordField `, [constantPattern], ' '); testSuggestions( - `from a | ${subExpression} dissect stringField ${constantPattern} `, + `from a | ${subExpression} dissect keywordField ${constantPattern} `, ['APPEND_SEPARATOR = $0', '| '], ' ' ); testSuggestions( - `from a | ${subExpression} dissect stringField ${constantPattern} append_separator = `, + `from a | ${subExpression} dissect keywordField ${constantPattern} append_separator = `, ['":"', '";"'] ); testSuggestions( - `from a | ${subExpression} dissect stringField ${constantPattern} append_separator = ":" `, + `from a | ${subExpression} dissect keywordField ${constantPattern} append_separator = ":" `, ['| '] ); } @@ -360,10 +352,10 @@ describe('autocomplete', () => { ...getFieldNamesByType('any').map((name) => `${name} `), ...getFunctionSignaturesByReturnType('sort', 'any', { scalar: true }), ]); - testSuggestions('from a | sort stringField ', ['ASC ', 'DESC ', ',', '| ']); - testSuggestions('from a | sort stringField desc ', ['NULLS FIRST ', 'NULLS LAST ', ',', '| ']); + testSuggestions('from a | sort keywordField ', ['ASC ', 'DESC ', ',', '| ']); + testSuggestions('from a | sort keywordField desc ', ['NULLS FIRST ', 'NULLS LAST ', ',', '| ']); // @TODO: improve here - // testSuggestions('from a | sort stringField desc ', ['first', 'last']); + // testSuggestions('from a | sort keywordField desc ', ['first', 'last']); }); describe('limit', () => { @@ -378,21 +370,21 @@ describe('autocomplete', () => { describe('rename', () => { testSuggestions('from a | rename ', getFieldNamesByType('any')); - testSuggestions('from a | rename stringField ', ['AS $0'], ' '); - testSuggestions('from a | rename stringField as ', ['var0']); + testSuggestions('from a | rename keywordField ', ['AS $0'], ' '); + testSuggestions('from a | rename keywordField as ', ['var0']); }); for (const command of ['keep', 'drop']) { describe(command, () => { testSuggestions(`from a | ${command} `, getFieldNamesByType('any')); testSuggestions( - `from a | ${command} stringField, `, - getFieldNamesByType('any').filter((name) => name !== 'stringField') + `from a | ${command} keywordField, `, + getFieldNamesByType('any').filter((name) => name !== 'keywordField') ); testSuggestions( - `from a | ${command} stringField,`, - getFieldNamesByType('any').filter((name) => name !== 'stringField'), + `from a | ${command} keywordField,`, + getFieldNamesByType('any').filter((name) => name !== 'keywordField'), ',' ); @@ -431,20 +423,7 @@ describe('autocomplete', () => { testSuggestions(`from a ${prevCommand}| enrich _${camelCase(mode)}:`, policyNames, ':'); } testSuggestions(`from a ${prevCommand}| enrich policy `, ['ON $0', 'WITH $0', '| ']); - testSuggestions(`from a ${prevCommand}| enrich policy on `, [ - 'keywordField', - 'stringField', - 'doubleField', - 'dateField', - 'booleanField', - 'ipField', - 'geoPointField', - 'geoShapeField', - 'cartesianPointField', - 'cartesianShapeField', - '`any#Char$Field`', - 'kubernetes.something.something', - ]); + testSuggestions(`from a ${prevCommand}| enrich policy on `, getFieldNamesByType('any')); testSuggestions(`from a ${prevCommand}| enrich policy on b `, ['WITH $0', ',', '| ']); testSuggestions( `from a ${prevCommand}| enrich policy on b with `, @@ -455,21 +434,21 @@ describe('autocomplete', () => { testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = `, [ ...getPolicyFields('policy'), ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField `, [ + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = keywordField `, [ ',', '| ', ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField, `, [ + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = keywordField, `, [ 'var1 = ', ...getPolicyFields('policy'), ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField, var1 `, [ + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = keywordField, var1 `, [ '= $0', ',', '| ', ]); testSuggestions( - `from a ${prevCommand}| enrich policy on b with var0 = stringField, var1 = `, + `from a ${prevCommand}| enrich policy on b with var0 = keywordField, var1 = `, [...getPolicyFields('policy')] ); testSuggestions( @@ -477,7 +456,7 @@ describe('autocomplete', () => { ['var0 = ', ...getPolicyFields('policy')], ' ' ); - testSuggestions(`from a ${prevCommand}| enrich policy with stringField `, [ + testSuggestions(`from a ${prevCommand}| enrich policy with keywordField `, [ '= $0', ',', '| ', @@ -498,8 +477,8 @@ describe('autocomplete', () => { ',', '| ', ]); - testSuggestions('from index | EVAL stringField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); - testSuggestions('from index | EVAL stringField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | EVAL keywordField not ', ['LIKE $0', 'RLIKE $0', 'IN $0']); + testSuggestions('from index | EVAL keywordField NOT ', ['LIKE $0', 'RLIKE $0', 'IN $0']); testSuggestions('from index | EVAL doubleField in ', ['( $0 )']); testSuggestions( 'from index | EVAL doubleField in ( )', @@ -527,17 +506,17 @@ describe('autocomplete', () => { ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ]); // Skip this test until the insensitive case equality gets restored back - testSuggestions.skip('from a | eval a=stringField =~ ', [ - ...getFieldNamesByType('string'), - ...getFunctionSignaturesByReturnType('eval', 'string', { scalar: true }), + testSuggestions.skip('from a | eval a=keywordField =~ ', [ + ...getFieldNamesByType(ESQL_STRING_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_STRING_TYPES, { scalar: true }), ]); testSuggestions( 'from a | eval a=round()', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] @@ -588,6 +567,7 @@ describe('autocomplete', () => { testSuggestions( 'from a | eval round(doubleField, ', [ + ...getFieldNamesByType('integer'), ...getFunctionSignaturesByReturnType('eval', 'integer', { scalar: true }, undefined, [ 'round', ]), @@ -601,23 +581,31 @@ describe('autocomplete', () => { ...getFunctionSignaturesByReturnType('eval', 'any', { scalar: true }), ]); testSuggestions('from a | eval a=round(doubleField) + ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions('from a | eval a=round(doubleField)+ ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions('from a | eval a=doubleField+ ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions('from a | eval a=`any#Char$Field`+ ', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), - ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { scalar: true }), + ...getFieldNamesByType(ESQL_COMMON_NUMERIC_TYPES), + ...getFunctionSignaturesByReturnType('eval', ESQL_COMMON_NUMERIC_TYPES, { + scalar: true, + }), ]); testSuggestions( - 'from a | stats avg(doubleField) by stringField | eval ', + 'from a | stats avg(doubleField) by keywordField | eval ', [ 'var0 = ', '`avg(doubleField)`', @@ -639,7 +627,7 @@ describe('autocomplete', () => { ' ' ); testSuggestions( - 'from a | stats avg(doubleField) by stringField | eval ', + 'from a | stats avg(doubleField) by keywordField | eval ', [ 'var0 = ', '`avg(doubleField)`', @@ -651,7 +639,7 @@ describe('autocomplete', () => { [[{ name: 'avg_doubleField_', type: 'double' }], undefined, undefined] ); testSuggestions( - 'from a | stats avg(doubleField), avg(kubernetes.something.something) by stringField | eval ', + 'from a | stats avg(doubleField), avg(kubernetes.something.something) by keywordField | eval ', [ 'var0 = ', '`avg(doubleField)`', @@ -670,13 +658,14 @@ describe('autocomplete', () => { undefined, ] ); + testSuggestions( 'from a | eval a=round(doubleField), b=round()', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] @@ -713,14 +702,10 @@ describe('autocomplete', () => { testSuggestions( 'from a | eval a=cidr_match(ipField, textField, ', [ - ...getFieldNamesByType('keyword'), - ...getFunctionSignaturesByReturnType( - 'eval', - ['text', 'keyword'], - { scalar: true }, - undefined, - ['cidr_match'] - ), + ...getFieldNamesByType('text'), + ...getFunctionSignaturesByReturnType('eval', 'text', { scalar: true }, undefined, [ + 'cidr_match', + ]), ], ' ' ); @@ -749,14 +734,15 @@ describe('autocomplete', () => { // round(round( // round(round(round( // etc... + for (const nesting of [1, 2, 3, 4]) { testSuggestions( `from a | eval a=${Array(nesting).fill('round(').join('')}`, [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(roundParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + roundParameterTypes, { scalar: true }, undefined, ['round'] @@ -766,6 +752,8 @@ describe('autocomplete', () => { ); } + const absParameterTypes = ['double', 'integer', 'long', 'unsigned_long'] as const; + // Smoke testing for suggestions in previous position than the end of the statement testSuggestions( 'from a | eval var0 = abs(doubleField) | eval abs(var0)', @@ -782,10 +770,10 @@ describe('autocomplete', () => { testSuggestions( 'from a | eval var0 = abs(b) | eval abs(var0)', [ - ...getFieldNamesByType(ESQL_NUMERIC_TYPES), + ...getFieldNamesByType(absParameterTypes), ...getFunctionSignaturesByReturnType( 'eval', - ESQL_NUMERIC_TYPES, + absParameterTypes, { scalar: true }, undefined, ['abs'] @@ -798,7 +786,7 @@ describe('autocomplete', () => { // Test suggestions for each possible param, within each signature variation, for each function for (const fn of evalFunctionDefinitions) { // skip this fn for the moment as it's quite hard to test - if (!['bucket', 'date_extract', 'date_diff'].includes(fn.name)) { + if (!['bucket', 'date_extract', 'date_diff', 'case'].includes(fn.name)) { for (const signature of fn.signatures) { signature.params.forEach((param, i) => { if (i < signature.params.length) { @@ -816,11 +804,11 @@ describe('autocomplete', () => { // get all possible types for this param const [constantOnlyParamDefs, acceptsFieldParamDefs] = partition( allParamDefs, - (p) => p.constantOnly || /_literal/.test(p.type) + (p) => p.constantOnly || /_literal/.test(p.type as string) ); - const getTypesFromParamDefs = (paramDefs: FunctionParameter[]) => - Array.from(new Set(paramDefs.map((p) => p.type))); + const getTypesFromParamDefs = (paramDefs: FunctionParameter[]): SupportedDataType[] => + Array.from(new Set(paramDefs.map((p) => p.type))).filter(isSupportedDataType); const suggestedConstants = param.literalSuggestions || param.literalOptions; @@ -837,8 +825,12 @@ describe('autocomplete', () => { suggestedConstants?.length ? suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ', ' : ''}`) : [ - ...getDateLiteralsByFieldType(getTypesFromParamDefs(acceptsFieldParamDefs)), - ...getFieldNamesByType(getTypesFromParamDefs(acceptsFieldParamDefs)), + ...getDateLiteralsByFieldType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), + ...getFieldNamesByType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), ...getFunctionSignaturesByReturnType( 'eval', getTypesFromParamDefs(acceptsFieldParamDefs), @@ -857,8 +849,12 @@ describe('autocomplete', () => { suggestedConstants?.length ? suggestedConstants.map((option) => `"${option}"${requiresMoreArgs ? ', ' : ''}`) : [ - ...getDateLiteralsByFieldType(getTypesFromParamDefs(acceptsFieldParamDefs)), - ...getFieldNamesByType(getTypesFromParamDefs(acceptsFieldParamDefs)), + ...getDateLiteralsByFieldType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), + ...getFieldNamesByType( + getTypesFromParamDefs(acceptsFieldParamDefs).filter(isFieldType) + ), ...getFunctionSignaturesByReturnType( 'eval', getTypesFromParamDefs(acceptsFieldParamDefs), @@ -967,7 +963,7 @@ describe('autocomplete', () => { describe('callbacks', () => { it('should send the fields query without the last command', async () => { const callbackMocks = createCustomCallbackMocks(undefined, undefined, undefined); - const statement = 'from a | drop stringField | eval var0 = abs(doubleField) '; + const statement = 'from a | drop keywordField | eval var0 = abs(doubleField) '; const triggerOffset = statement.lastIndexOf(' '); const context = createCompletionContext(statement[triggerOffset]); await suggest( @@ -978,7 +974,7 @@ describe('autocomplete', () => { callbackMocks ); expect(callbackMocks.getFieldsFor).toHaveBeenCalledWith({ - query: 'from a | drop stringField', + query: 'from a | drop keywordField', }); }); it('should send the fields query aware of the location', async () => { @@ -1101,7 +1097,13 @@ describe('autocomplete', () => { ); // DISSECT field - testSuggestions('FROM index1 | DISSECT b', getFieldNamesByType('string'), undefined, 23); + // enable once https://github.com/elastic/kibana/issues/190070 is fixed + testSuggestions.skip( + 'FROM index1 | DISSECT b', + getFieldNamesByType(ESQL_STRING_TYPES), + undefined, + 23 + ); // DROP (first field) testSuggestions('FROM index1 | DROP f', getFieldNamesByType('any'), undefined, 20); @@ -1139,7 +1141,13 @@ describe('autocomplete', () => { ); // GROK field - testSuggestions('FROM index1 | GROK f', getFieldNamesByType('string'), undefined, 20); + // enable once https://github.com/elastic/kibana/issues/190070 + testSuggestions.skip( + 'FROM index1 | GROK f', + getFieldNamesByType(ESQL_STRING_TYPES), + undefined, + 20 + ); // KEEP (first field) testSuggestions('FROM index1 | KEEP f', getFieldNamesByType('any'), undefined, 20); @@ -1182,18 +1190,18 @@ describe('autocomplete', () => { // SORT field order testSuggestions( - 'FROM index1 | SORT stringField a', + 'FROM index1 | SORT keywordField a', ['ASC ', 'DESC ', ',', '| '], undefined, - 32 + 33 ); // SORT field order nulls testSuggestions( - 'FROM index1 | SORT stringField ASC n', + 'FROM index1 | SORT keywordField ASC n', ['NULLS FIRST ', 'NULLS LAST ', ',', '| '], undefined, - 36 + 37 ); // STATS argument @@ -1235,17 +1243,18 @@ describe('autocomplete', () => { // WHERE argument comparison testSuggestions( - 'FROM index1 | WHERE stringField i', + 'FROM index1 | WHERE keywordField i', getFunctionSignaturesByReturnType( 'where', 'boolean', { builtin: true, }, - ['string'] + undefined, + ['and', 'or', 'not'] ), undefined, - 33 + 34 ); }); @@ -1299,12 +1308,16 @@ describe('autocomplete', () => { // field parameter const expectedStringSuggestionsWhenMoreArgsAreNeeded = [ - ...getFieldNamesByType('keyword') + ...getFieldNamesByType(ESQL_STRING_TYPES) .map((field) => `${field}, `) .map(attachTriggerCommand), - ...getFunctionSignaturesByReturnType('eval', 'keyword', { scalar: true }, undefined, [ - 'replace', - ]).map((s) => ({ + ...getFunctionSignaturesByReturnType( + 'eval', + ESQL_STRING_TYPES, + { scalar: true }, + undefined, + ['replace'] + ).map((s) => ({ ...s, text: `${s.text},`, })), @@ -1319,23 +1332,30 @@ describe('autocomplete', () => { // subsequent parameter testSuggestions( - 'FROM a | EVAL REPLACE(stringField, )', + 'FROM a | EVAL REPLACE(keywordField, )', expectedStringSuggestionsWhenMoreArgsAreNeeded, undefined, - 35 + 36 ); // final parameter — should not advance! testSuggestions( - 'FROM a | EVAL REPLACE(stringField, stringField, )', + 'FROM a | EVAL REPLACE(keywordField, keywordField, )', [ - ...getFieldNamesByType('keyword').map((field) => ({ text: field, command: undefined })), - ...getFunctionSignaturesByReturnType('eval', 'keyword', { scalar: true }, undefined, [ - 'replace', - ]), + ...getFieldNamesByType(ESQL_STRING_TYPES).map((field) => ({ + text: field, + command: undefined, + })), + ...getFunctionSignaturesByReturnType( + 'eval', + ESQL_STRING_TYPES, + { scalar: true }, + undefined, + ['replace'] + ), ], undefined, - 48 + 50 ); // Trigger character because this is how it will actually be... the user will press @@ -1495,17 +1515,17 @@ describe('autocomplete', () => { // WHERE argument comparison testSuggestions( - 'FROM a | WHERE stringField ', + 'FROM a | WHERE keywordField ', getFunctionSignaturesByReturnType( 'where', 'boolean', { builtin: true, }, - ['string'] + ['keyword'] ).map((s) => (s.text.toLowerCase().includes('null') ? s : attachTriggerCommand(s))), undefined, - 27 + 28 ); }); }); diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts index 67af737d34da0..36d3664b5617a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.ts @@ -95,7 +95,7 @@ import { isAggFunctionUsedAlready, removeQuoteForSuggestedSources, } from './helper'; -import { FunctionParameter, FunctionReturnType, SupportedFieldType } from '../definitions/types'; +import { FunctionParameter, FunctionReturnType, SupportedDataType } from '../definitions/types'; type GetSourceFn = () => Promise; type GetDataStreamsForIntegrationFn = ( @@ -441,7 +441,7 @@ function extractFinalTypeFromArg( references: Pick ): | ESQLLiteral['literalType'] - | SupportedFieldType + | SupportedDataType | FunctionReturnType | 'timeInterval' | string // @TODO remove this @@ -787,7 +787,7 @@ async function getExpressionSuggestionsByType( option, argDef, nodeArg, - nodeArgType || 'any', + (nodeArgType as string) || 'any', references, getFieldsByType )) @@ -882,7 +882,7 @@ async function getExpressionSuggestionsByType( option, argDef, nodeArg, - nodeArgType, + nodeArgType as string, references, getFieldsByType )) @@ -1024,11 +1024,15 @@ async function getBuiltinFunctionNextArgument( if (isFnComplete.reason === 'fewArgs') { const fnDef = getFunctionDefinition(nodeArg.name); - if (fnDef?.signatures.every(({ params }) => params.some(({ type }) => isArrayType(type)))) { + if ( + fnDef?.signatures.every(({ params }) => + params.some(({ type }) => isArrayType(type as string)) + ) + ) { suggestions.push(listCompleteItem); } else { const finalType = nestedType || nodeArgType || 'any'; - const supportedTypes = getSupportedTypesForBinaryOperators(fnDef, finalType); + const supportedTypes = getSupportedTypesForBinaryOperators(fnDef, finalType as string); suggestions.push( ...(await getFieldsOrFunctionsSuggestions( // this is a special case with AND/OR @@ -1037,7 +1041,7 @@ async function getBuiltinFunctionNextArgument( // to actually suggest a wider set of fields/functions finalType === 'boolean' && getFunctionDefinition(nodeArg.name)?.type === 'builtin' ? ['any'] - : supportedTypes, + : (supportedTypes as string[]), command.name, option?.name, getFieldsByType, @@ -1146,11 +1150,7 @@ async function getFieldsOrFunctionsSuggestions( } } - // could also be in stats (bucket) but our autocomplete is not great yet - const displayDateSuggestions = types.includes('date') && ['where', 'eval'].includes(commandName); - const suggestions = filteredFieldsByType.concat( - displayDateSuggestions ? getDateLiterals() : [], functions ? getCompatibleFunctionDefinition(commandName, optionName, types, ignoreFn) : [], variables ? pushItUpInTheList(buildVariablesDefinitions(filteredVariablesByType), functions) @@ -1321,7 +1321,7 @@ async function getFunctionArgsSuggestions( // const [constantOnlyParamDefs, paramDefsWhichSupportFields] = partition( allParamDefinitionsForThisPosition, - (paramDef) => paramDef.constantOnly || /_literal$/.test(paramDef.type) + (paramDef) => paramDef.constantOnly || /_literal$/.test(paramDef.type as string) ); const getTypesFromParamDefs = (paramDefs: FunctionParameter[]) => { @@ -1332,7 +1332,7 @@ async function getFunctionArgsSuggestions( suggestions.push( ...getCompatibleLiterals( command.name, - getTypesFromParamDefs(constantOnlyParamDefs), + getTypesFromParamDefs(constantOnlyParamDefs) as string[], undefined, { addComma: shouldAddComma, advanceCursorAndOpenSuggestions: hasMoreMandatoryArgs } ) @@ -1341,7 +1341,7 @@ async function getFunctionArgsSuggestions( // Fields suggestions.push( ...pushItUpInTheList( - await getFieldsByType(getTypesFromParamDefs(paramDefsWhichSupportFields), [], { + await getFieldsByType(getTypesFromParamDefs(paramDefsWhichSupportFields) as string[], [], { addComma: shouldAddComma, advanceCursorAndOpenSuggestions: hasMoreMandatoryArgs, }), @@ -1354,7 +1354,7 @@ async function getFunctionArgsSuggestions( ...getCompatibleFunctionDefinition( command.name, option?.name, - getTypesFromParamDefs(paramDefsWhichSupportFields), + getTypesFromParamDefs(paramDefsWhichSupportFields) as string[], fnToIgnore ).map((suggestion) => ({ ...suggestion, @@ -1435,7 +1435,7 @@ async function getListArgsSuggestions( const otherArgs = node.args.filter(Array.isArray).flat().filter(isColumnItem); suggestions.push( ...(await getFieldsOrFunctionsSuggestions( - [argType], + [argType as string], command.name, undefined, getFieldsByType, @@ -1639,7 +1639,7 @@ async function getOptionArgsSuggestions( option, { type: argDef?.type || 'any' }, nodeArg, - nodeArgType, + nodeArgType as string, references, getFieldsByType )) diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts index 88acf1c207f09..4f7e919c84361 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/complete_items.ts @@ -16,6 +16,7 @@ import { TRIGGER_SUGGESTION_COMMAND, buildConstantsDefinitions, } from './factories'; +import { FunctionParameterType, FunctionReturnType } from '../definitions/types'; export function getAssignmentDefinitionCompletitionItem() { const assignFn = builtinFunctions.find(({ name }) => name === '=')!; @@ -54,8 +55,8 @@ export const getNextTokenForNot = ( export const getBuiltinCompatibleFunctionDefinition = ( command: string, option: string | undefined, - argType: string, - returnTypes?: string[], + argType: FunctionParameterType, + returnTypes?: FunctionReturnType[], { skipAssign }: { skipAssign?: boolean } = {} ): SuggestionRawDefinition[] => { const compatibleFunctions = builtinFunctions.filter( diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts index 13a3c76b389f0..c9d0185d5c301 100644 --- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts +++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/factories.ts @@ -102,7 +102,8 @@ export const getCompatibleFunctionDefinition = ( return fnSupportedByCommand .filter((mathDefinition) => mathDefinition.signatures.some( - (signature) => returnTypes[0] === 'any' || returnTypes.includes(signature.returnType) + (signature) => + returnTypes[0] === 'any' || returnTypes.includes(signature.returnType as string) ) ) .map(getSuggestionFunctionDefinition); diff --git a/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts b/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts index a4ff739297201..cca1197ed629b 100644 --- a/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/code_actions/actions.test.ts @@ -11,32 +11,39 @@ import { validateQuery } from '../validation/validation'; import { getAllFunctions } from '../shared/helpers'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; import { CodeActionOptions } from './types'; +import { ESQLRealField } from '../validation/types'; +import { FieldType } from '../definitions/types'; function getCallbackMocks() { return { - getFieldsFor: jest.fn(async ({ query }) => - /enrich/.test(query) - ? [ - { name: 'otherField', type: 'string' }, - { name: 'yetAnotherField', type: 'number' }, - ] - : /unsupported_index/.test(query) - ? [{ name: 'unsupported_field', type: 'unsupported' }] - : [ - ...['string', 'number', 'date', 'boolean', 'ip'].map((type) => ({ - name: `${type}Field`, - type, - })), - { name: 'geoPointField', type: 'geo_point' }, - { name: 'any#Char$Field', type: 'number' }, - { name: 'kubernetes.something.something', type: 'number' }, - { - name: `listField`, - type: `list`, - }, - { name: '@timestamp', type: 'date' }, - ] - ), + getFieldsFor: jest.fn, any>(async ({ query }) => { + if (/enrich/.test(query)) { + const fields: ESQLRealField[] = [ + { name: 'otherField', type: 'keyword' }, + { name: 'yetAnotherField', type: 'double' }, + ]; + return fields; + } + + if (/unsupported_index/.test(query)) { + const fields: ESQLRealField[] = [{ name: 'unsupported_field', type: 'unsupported' }]; + return fields; + } + + const localDataTypes: FieldType[] = ['keyword', 'double', 'date', 'boolean', 'ip']; + const fields: ESQLRealField[] = [ + ...localDataTypes.map((type) => ({ + name: `${type}Field`, + type, + })), + { name: 'geoPointField', type: 'geo_point' }, + { name: 'any#Char$Field', type: 'double' }, + { name: 'kubernetes.something.something', type: 'double' }, + { name: '@timestamp', type: 'date' }, + ]; + + return fields; + }), getSources: jest.fn(async () => ['index', '.secretIndex', 'my-index'].map((name) => ({ name, @@ -162,29 +169,29 @@ describe('quick fixes logic', () => { { relaxOnMissingCallbacks: false }, ]) { for (const command of ['KEEP', 'DROP', 'EVAL']) { - testQuickFixes(`FROM index | ${command} stringField`, [], options); - // strongField => stringField - testQuickFixes(`FROM index | ${command} strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ${command} keywordField`, [], options); + // koywordField => keywordField + testQuickFixes(`FROM index | ${command} koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ${command} numberField, strongField`, - ['stringField'], + `FROM index | ${command} numberField, koywordField`, + ['keywordField'], options ); } - testQuickFixes(`FROM index | EVAL round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | EVAL var0 = round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE round(strongField) > 0`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE 0 < round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | RENAME strongField as newField`, ['stringField'], options); + testQuickFixes(`FROM index | EVAL round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | EVAL var0 = round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE round(koywordField) > 0`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE 0 < round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | RENAME koywordField as newField`, ['keywordField'], options); // This levarage the knowledge of the enrich policy fields to suggest the right field testQuickFixes( `FROM index | ENRICH policy | KEEP yetAnotherField2`, ['yetAnotherField'], options ); - testQuickFixes(`FROM index | ENRICH policy ON strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ENRICH policy ON koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ENRICH policy ON stringField WITH yetAnotherField2`, + `FROM index | ENRICH policy ON keywordField WITH yetAnotherField2`, ['yetAnotherField'], options ); @@ -209,29 +216,29 @@ describe('quick fixes logic', () => { { relaxOnMissingCallbacks: false }, ]) { for (const command of ['KEEP', 'DROP', 'EVAL']) { - testQuickFixes(`FROM index | ${command} stringField`, [], options); - // strongField => stringField - testQuickFixes(`FROM index | ${command} strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ${command} keywordField`, [], options); + // koywordField => keywordField + testQuickFixes(`FROM index | ${command} koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ${command} numberField, strongField`, - ['stringField'], + `FROM index | ${command} numberField, koywordField`, + ['keywordField'], options ); } - testQuickFixes(`FROM index | EVAL round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | EVAL var0 = round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE round(strongField) > 0`, ['stringField'], options); - testQuickFixes(`FROM index | WHERE 0 < round(strongField)`, ['stringField'], options); - testQuickFixes(`FROM index | RENAME strongField as newField`, ['stringField'], options); + testQuickFixes(`FROM index | EVAL round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | EVAL var0 = round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE round(koywordField) > 0`, ['keywordField'], options); + testQuickFixes(`FROM index | WHERE 0 < round(koywordField)`, ['keywordField'], options); + testQuickFixes(`FROM index | RENAME koywordField as newField`, ['keywordField'], options); // This levarage the knowledge of the enrich policy fields to suggest the right field testQuickFixes( `FROM index | ENRICH policy | KEEP yetAnotherField2`, ['yetAnotherField'], options ); - testQuickFixes(`FROM index | ENRICH policy ON strongField`, ['stringField'], options); + testQuickFixes(`FROM index | ENRICH policy ON koywordField`, ['keywordField'], options); testQuickFixes( - `FROM index | ENRICH policy ON stringField WITH yetAnotherField2`, + `FROM index | ENRICH policy ON keywordField WITH yetAnotherField2`, ['yetAnotherField'], options ); @@ -329,8 +336,8 @@ describe('quick fixes logic', () => { { relaxOnMissingCallbacks: false }, { relaxOnMissingCallbacks: false }, ]) { - testQuickFixes(`FROM index | WHERE stringField like 'asda'`, ['"asda"'], options); - testQuickFixes(`FROM index | WHERE stringField not like 'asda'`, ['"asda"'], options); + testQuickFixes(`FROM index | WHERE keywordField like 'asda'`, ['"asda"'], options); + testQuickFixes(`FROM index | WHERE keywordField not like 'asda'`, ['"asda"'], options); } }); @@ -407,7 +414,7 @@ describe('quick fixes logic', () => { describe('callbacks', () => { it('should not crash if specific callback functions are not passed', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, @@ -427,7 +434,7 @@ describe('quick fixes logic', () => { it('should not crash if specific callback functions are not passed with relaxed option', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, @@ -453,7 +460,7 @@ describe('quick fixes logic', () => { it('should not crash no callbacks are passed', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, @@ -469,7 +476,7 @@ describe('quick fixes logic', () => { it('should not crash no callbacks are passed with relaxed option', async () => { const callbackMocks = getCallbackMocks(); - const statement = `from a | eval b = a | enrich policy | dissect stringField "%{firstWord}"`; + const statement = `from a | eval b = a | enrich policy | dissect keywordField "%{firstWord}"`; const { errors } = await validateQuery( statement, getAstAndSyntaxErrors, diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts index 10945d1c6b135..45d32d8e3282d 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts @@ -308,13 +308,13 @@ const comparisonFunctions: FunctionDefinition[] = [ { params: [ { name: 'left', type: 'boolean' as const }, - { name: 'right', type: 'string' as const, constantOnly: true }, + { name: 'right', type: 'keyword' as const, constantOnly: true }, ], returnType: 'boolean' as const, }, { params: [ - { name: 'left', type: 'string' as const, constantOnly: true }, + { name: 'left', type: 'keyword' as const, constantOnly: true }, { name: 'right', type: 'boolean' as const }, ], returnType: 'boolean' as const, @@ -338,13 +338,13 @@ const comparisonFunctions: FunctionDefinition[] = [ { params: [ { name: 'left', type: 'boolean' as const }, - { name: 'right', type: 'string' as const, constantOnly: true }, + { name: 'right', type: 'keyword' as const, constantOnly: true }, ], returnType: 'boolean' as const, }, { params: [ - { name: 'left', type: 'string' as const, constantOnly: true }, + { name: 'left', type: 'keyword' as const, constantOnly: true }, { name: 'right', type: 'boolean' as const }, ], returnType: 'boolean' as const, diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts index a44db712ab230..3773a7c30e3d1 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { CommandDefinition, FunctionDefinition } from './types'; +import type { CommandDefinition, FunctionDefinition, FunctionParameterType } from './types'; /** * Given a function definition, this function will return a list of function signatures @@ -104,7 +104,7 @@ export function printArguments( optional, }: { name: string; - type: string | string[]; + type: FunctionParameterType | FunctionParameterType[]; optional?: boolean; }, withTypes: boolean diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts index 660bb1c7aca81..398a26e7f152d 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/types.ts @@ -8,42 +8,67 @@ import type { ESQLCommand, ESQLCommandOption, ESQLFunction, ESQLMessage } from '@kbn/esql-ast'; -// Currently, partial of the full list -// https://github.com/elastic/elasticsearch/blob/main/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java -export const supportedFieldTypes = [ +/** + * All supported field types in ES|QL. This is all the types + * that can come back in the table from a query. + */ +export const fieldTypes = [ + 'boolean', + 'date', 'double', - 'unsigned_long', - 'long', + 'ip', + 'keyword', 'integer', - 'counter_integer', - 'counter_long', - 'counter_double', - 'date', - 'date_period', + 'long', 'text', - 'keyword', - 'boolean', - 'ip', + 'unsigned_long', + 'version', 'cartesian_point', 'cartesian_shape', 'geo_point', 'geo_shape', - 'version', + 'counter_integer', + 'counter_long', + 'counter_double', + 'unsupported', +] as const; + +export type FieldType = (typeof fieldTypes)[number]; + +export const isFieldType = (type: string | FunctionParameterType): type is FieldType => + fieldTypes.includes(type as FieldType); + +/** + * This is the list of all data types that are supported in ES|QL. + * + * Not all of these can be used as field types. Some can only be literals, + * others may be the value of a field, but cannot be used in the index mapping. + * + * This is a partial list. The full list is here and we may need to expand this type as + * the capabilities of the client-side engines grow. + * https://github.com/elastic/elasticsearch/blob/main/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DataType.java + */ +export const dataTypes = [ + ...fieldTypes, + 'null', + 'time_literal', // @TODO consider merging time_literal with time_duration + 'time_duration', + 'date_period', ] as const; -export const isSupportedFieldType = (type: string): type is SupportedFieldType => - supportedFieldTypes.includes(type as SupportedFieldType); +export type SupportedDataType = (typeof dataTypes)[number]; -export type SupportedFieldType = (typeof supportedFieldTypes)[number]; +export const isSupportedDataType = ( + type: string | FunctionParameterType +): type is SupportedDataType => dataTypes.includes(type as SupportedDataType); -export type FunctionParameterType = - | SupportedFieldType - | 'string' - | 'null' - | 'any' - | 'chrono_literal' - | 'time_literal' - | 'time_duration' +/** + * This is a set of array types. These aren't official ES|QL types, but they are + * currently used in the function definitions in a couple of specific scenarios. + * + * The fate of these is uncertain. They may be removed in the future. + */ +type ArrayType = | 'double[]' | 'unsigned_long[]' | 'long[]' @@ -51,38 +76,22 @@ export type FunctionParameterType = | 'counter_integer[]' | 'counter_long[]' | 'counter_double[]' - | 'string[]' | 'keyword[]' | 'text[]' | 'boolean[]' | 'any[]' - | 'datetime[]' + | 'date[]' | 'date_period[]'; -export type FunctionReturnType = - | 'double' - | 'unsigned_long' - | 'long' - | 'integer' - | 'int' - | 'counter_integer' - | 'counter_long' - | 'counter_double' - | 'date' - | 'date_period' - | 'time_duration' - | 'any' - | 'boolean' - | 'text' - | 'keyword' - | 'string' - | 'cartesian_point' - | 'cartesian_shape' - | 'geo_point' - | 'geo_shape' - | 'ip' - | 'version' - | 'void'; +/** + * This is the type of a parameter in a function definition. + */ +export type FunctionParameterType = Omit | ArrayType | 'any'; + +/** + * This is the return type of a function definition. + */ +export type FunctionReturnType = Omit | 'any' | 'void'; export interface FunctionDefinition { type: 'builtin' | 'agg' | 'eval'; diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts index 1c9c82f676087..4446b4e32908d 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts @@ -277,7 +277,6 @@ const arrayToSingularMap: Map = ne ['counter_integer[]', 'counter_integer'], ['counter_long[]', 'counter_long'], ['counter_double[]', 'counter_double'], - ['string[]', 'string'], ['keyword[]', 'keyword'], ['text[]', 'text'], ['datetime[]', 'date'], @@ -431,7 +430,7 @@ export function checkFunctionArgMatchesDefinition( return true; } if (arg.type === 'literal') { - const matched = compareLiteralType(argType, arg); + const matched = compareLiteralType(argType as string, arg); return matched; } if (arg.type === 'function') { @@ -457,7 +456,7 @@ export function checkFunctionArgMatchesDefinition( (ct) => ['any', 'null'].includes(ct) || argType === ct || - (ct === 'string' && ['text', 'keyword'].includes(argType)) + (ct === 'string' && ['text', 'keyword'].includes(argType as string)) ); } if (arg.type === 'inlineCast') { diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts b/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts index ee1a912c688ea..ca99615f31136 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/variables.ts @@ -107,7 +107,7 @@ function addVariableFromAssignment( const rightHandSideArgType = getAssignRightHandSideType(assignOperation.args[1], fields); addToVariableOccurrencies(variables, { name: assignOperation.args[0].name, - type: rightHandSideArgType || 'double' /* fallback to number */, + type: (rightHandSideArgType as string) || 'double' /* fallback to number */, location: assignOperation.args[0].location, }); } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index 5be61a8c873a2..84f16cedf03b9 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -9,56 +9,44 @@ ], "fields": [ { - "name": "doubleField", - "type": "double" - }, - { - "name": "unsignedLongField", - "type": "unsigned_long" - }, - { - "name": "longField", - "type": "long" + "name": "booleanField", + "type": "boolean" }, { - "name": "integerField", - "type": "integer" + "name": "dateField", + "type": "date" }, { - "name": "counterIntegerField", - "type": "counter_integer" + "name": "doubleField", + "type": "double" }, { - "name": "counterLongField", - "type": "counter_long" + "name": "ipField", + "type": "ip" }, { - "name": "counterDoubleField", - "type": "counter_double" + "name": "keywordField", + "type": "keyword" }, { - "name": "dateField", - "type": "date" + "name": "integerField", + "type": "integer" }, { - "name": "datePeriodField", - "type": "date_period" + "name": "longField", + "type": "long" }, { "name": "textField", "type": "text" }, { - "name": "keywordField", - "type": "keyword" - }, - { - "name": "booleanField", - "type": "boolean" + "name": "unsignedLongField", + "type": "unsigned_long" }, { - "name": "ipField", - "type": "ip" + "name": "versionField", + "type": "version" }, { "name": "cartesianPointField", @@ -77,8 +65,16 @@ "type": "geo_shape" }, { - "name": "versionField", - "type": "version" + "name": "counterIntegerField", + "type": "counter_integer" + }, + { + "name": "counterLongField", + "type": "counter_long" + }, + { + "name": "counterDoubleField", + "type": "counter_double" }, { "name": "any#Char$Field", @@ -2323,13 +2319,6 @@ ], "warning": [] }, - { - "query": "from unsupported_index | keep unsupported_field", - "error": [], - "warning": [ - "Field [unsupported_field] cannot be retrieved, it is unsupported or not indexed; returning null" - ] - }, { "query": "FROM index | STATS ROUND(AVG(doubleField * 1.5)), COUNT(*), MIN(doubleField * 10) | KEEP `MIN(doubleField * 10)`", "error": [], @@ -3721,362 +3710,282 @@ "warning": [] }, { - "query": "from a_index | where doubleField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where doubleField Is nOt NuLL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where unsignedLongField Is nOt NuLL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where longField IS NULL", + "query": "from a_index | where booleanField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS null", + "query": "from a_index | where booleanField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where longField is null", + "query": "from a_index | where booleanField is null", "error": [], "warning": [] }, { - "query": "from a_index | where longField is NULL", + "query": "from a_index | where booleanField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS NOT NULL", + "query": "from a_index | where booleanField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS NOT null", + "query": "from a_index | where booleanField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where longField IS not NULL", + "query": "from a_index | where booleanField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where longField Is nOt NuLL", + "query": "from a_index | where booleanField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS NULL", + "query": "from a_index | where dateField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS null", + "query": "from a_index | where dateField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where integerField is null", + "query": "from a_index | where dateField is null", "error": [], "warning": [] }, { - "query": "from a_index | where integerField is NULL", + "query": "from a_index | where dateField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS NOT NULL", + "query": "from a_index | where dateField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS NOT null", + "query": "from a_index | where dateField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where integerField IS not NULL", + "query": "from a_index | where dateField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where integerField Is nOt NuLL", + "query": "from a_index | where dateField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS NULL", + "query": "from a_index | where doubleField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS null", + "query": "from a_index | where doubleField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField is null", + "query": "from a_index | where doubleField is null", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField is NULL", + "query": "from a_index | where doubleField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS NOT NULL", + "query": "from a_index | where doubleField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS NOT null", + "query": "from a_index | where doubleField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField IS not NULL", + "query": "from a_index | where doubleField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterIntegerField Is nOt NuLL", + "query": "from a_index | where doubleField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS NULL", + "query": "from a_index | where ipField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS null", + "query": "from a_index | where ipField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField is null", + "query": "from a_index | where ipField is null", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField is NULL", + "query": "from a_index | where ipField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS NOT NULL", + "query": "from a_index | where ipField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS NOT null", + "query": "from a_index | where ipField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField IS not NULL", + "query": "from a_index | where ipField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterLongField Is nOt NuLL", + "query": "from a_index | where ipField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS NULL", + "query": "from a_index | where keywordField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS null", + "query": "from a_index | where keywordField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField is null", + "query": "from a_index | where keywordField is null", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField is NULL", + "query": "from a_index | where keywordField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS NOT NULL", + "query": "from a_index | where keywordField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS NOT null", + "query": "from a_index | where keywordField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField IS not NULL", + "query": "from a_index | where keywordField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where counterDoubleField Is nOt NuLL", + "query": "from a_index | where keywordField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS NULL", + "query": "from a_index | where integerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS null", + "query": "from a_index | where integerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where dateField is null", + "query": "from a_index | where integerField is null", "error": [], "warning": [] }, { - "query": "from a_index | where dateField is NULL", + "query": "from a_index | where integerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS NOT NULL", + "query": "from a_index | where integerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS NOT null", + "query": "from a_index | where integerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where dateField IS not NULL", + "query": "from a_index | where integerField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where dateField Is nOt NuLL", + "query": "from a_index | where integerField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS NULL", + "query": "from a_index | where longField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS null", + "query": "from a_index | where longField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField is null", + "query": "from a_index | where longField is null", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField is NULL", + "query": "from a_index | where longField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS NOT NULL", + "query": "from a_index | where longField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS NOT null", + "query": "from a_index | where longField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField IS not NULL", + "query": "from a_index | where longField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where datePeriodField Is nOt NuLL", + "query": "from a_index | where longField Is nOt NuLL", "error": [], "warning": [] }, @@ -4121,122 +4030,82 @@ "warning": [] }, { - "query": "from a_index | where keywordField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where keywordField Is nOt NuLL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | where booleanField IS NULL", + "query": "from a_index | where unsignedLongField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS null", + "query": "from a_index | where unsignedLongField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField is null", + "query": "from a_index | where unsignedLongField is null", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField is NULL", + "query": "from a_index | where unsignedLongField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS NOT NULL", + "query": "from a_index | where unsignedLongField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS NOT null", + "query": "from a_index | where unsignedLongField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField IS not NULL", + "query": "from a_index | where unsignedLongField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where booleanField Is nOt NuLL", + "query": "from a_index | where unsignedLongField Is nOt NuLL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS NULL", + "query": "from a_index | where versionField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS null", + "query": "from a_index | where versionField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where ipField is null", + "query": "from a_index | where versionField is null", "error": [], "warning": [] }, { - "query": "from a_index | where ipField is NULL", + "query": "from a_index | where versionField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS NOT NULL", + "query": "from a_index | where versionField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS NOT null", + "query": "from a_index | where versionField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where ipField IS not NULL", + "query": "from a_index | where versionField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where ipField Is nOt NuLL", + "query": "from a_index | where versionField Is nOt NuLL", "error": [], "warning": [] }, @@ -4401,42 +4270,122 @@ "warning": [] }, { - "query": "from a_index | where versionField IS NULL", + "query": "from a_index | where counterIntegerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS null", + "query": "from a_index | where counterIntegerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | where versionField is null", + "query": "from a_index | where counterIntegerField is null", "error": [], "warning": [] }, { - "query": "from a_index | where versionField is NULL", + "query": "from a_index | where counterIntegerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS NOT NULL", + "query": "from a_index | where counterIntegerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS NOT null", + "query": "from a_index | where counterIntegerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | where versionField IS not NULL", + "query": "from a_index | where counterIntegerField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | where versionField Is nOt NuLL", + "query": "from a_index | where counterIntegerField Is nOt NuLL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterLongField Is nOt NuLL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | where counterDoubleField Is nOt NuLL", "error": [], "warning": [] }, @@ -4572,317 +4521,247 @@ "warning": [] }, { - "query": "from a_index | eval doubleField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval doubleField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval unsignedLongField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval longField IS NULL", + "query": "from a_index | eval booleanField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS null", + "query": "from a_index | eval booleanField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval longField is null", + "query": "from a_index | eval booleanField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval longField is NULL", + "query": "from a_index | eval booleanField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS NOT NULL", + "query": "from a_index | eval booleanField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS NOT null", + "query": "from a_index | eval booleanField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval longField IS not NULL", + "query": "from a_index | eval booleanField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS NULL", + "query": "from a_index | eval dateField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS null", + "query": "from a_index | eval dateField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField is null", + "query": "from a_index | eval dateField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField is NULL", + "query": "from a_index | eval dateField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS NOT NULL", + "query": "from a_index | eval dateField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS NOT null", + "query": "from a_index | eval dateField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval integerField IS not NULL", + "query": "from a_index | eval dateField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS NULL", + "query": "from a_index | eval doubleField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS null", + "query": "from a_index | eval doubleField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField is null", + "query": "from a_index | eval doubleField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField is NULL", + "query": "from a_index | eval doubleField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS NOT NULL", + "query": "from a_index | eval doubleField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS NOT null", + "query": "from a_index | eval doubleField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterIntegerField IS not NULL", + "query": "from a_index | eval doubleField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS NULL", + "query": "from a_index | eval ipField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS null", + "query": "from a_index | eval ipField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField is null", + "query": "from a_index | eval ipField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField is NULL", + "query": "from a_index | eval ipField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS NOT NULL", + "query": "from a_index | eval ipField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS NOT null", + "query": "from a_index | eval ipField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterLongField IS not NULL", + "query": "from a_index | eval ipField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS NULL", + "query": "from a_index | eval keywordField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS null", + "query": "from a_index | eval keywordField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField is null", + "query": "from a_index | eval keywordField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField is NULL", + "query": "from a_index | eval keywordField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS NOT NULL", + "query": "from a_index | eval keywordField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS NOT null", + "query": "from a_index | eval keywordField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval counterDoubleField IS not NULL", + "query": "from a_index | eval keywordField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS NULL", + "query": "from a_index | eval integerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS null", + "query": "from a_index | eval integerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField is null", + "query": "from a_index | eval integerField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField is NULL", + "query": "from a_index | eval integerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS NOT NULL", + "query": "from a_index | eval integerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS NOT null", + "query": "from a_index | eval integerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval dateField IS not NULL", + "query": "from a_index | eval integerField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS NULL", + "query": "from a_index | eval longField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS null", + "query": "from a_index | eval longField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField is null", + "query": "from a_index | eval longField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField is NULL", + "query": "from a_index | eval longField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS NOT NULL", + "query": "from a_index | eval longField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS NOT null", + "query": "from a_index | eval longField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval datePeriodField IS not NULL", + "query": "from a_index | eval longField IS not NULL", "error": [], "warning": [] }, @@ -4922,107 +4801,72 @@ "warning": [] }, { - "query": "from a_index | eval keywordField IS NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField is null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField is NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS NOT NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS NOT null", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval keywordField IS not NULL", - "error": [], - "warning": [] - }, - { - "query": "from a_index | eval booleanField IS NULL", + "query": "from a_index | eval unsignedLongField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS null", + "query": "from a_index | eval unsignedLongField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField is null", + "query": "from a_index | eval unsignedLongField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField is NULL", + "query": "from a_index | eval unsignedLongField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS NOT NULL", + "query": "from a_index | eval unsignedLongField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS NOT null", + "query": "from a_index | eval unsignedLongField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval booleanField IS not NULL", + "query": "from a_index | eval unsignedLongField IS not NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS NULL", + "query": "from a_index | eval versionField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS null", + "query": "from a_index | eval versionField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField is null", + "query": "from a_index | eval versionField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField is NULL", + "query": "from a_index | eval versionField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS NOT NULL", + "query": "from a_index | eval versionField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS NOT null", + "query": "from a_index | eval versionField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval ipField IS not NULL", + "query": "from a_index | eval versionField IS not NULL", "error": [], "warning": [] }, @@ -5167,37 +5011,107 @@ "warning": [] }, { - "query": "from a_index | eval versionField IS NULL", + "query": "from a_index | eval counterIntegerField IS NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS null", + "query": "from a_index | eval counterIntegerField IS null", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField is null", + "query": "from a_index | eval counterIntegerField is null", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField is NULL", + "query": "from a_index | eval counterIntegerField is NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS NOT NULL", + "query": "from a_index | eval counterIntegerField IS NOT NULL", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS NOT null", + "query": "from a_index | eval counterIntegerField IS NOT null", "error": [], "warning": [] }, { - "query": "from a_index | eval versionField IS not NULL", + "query": "from a_index | eval counterIntegerField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterLongField IS not NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField is null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField is NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS NOT NULL", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS NOT null", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval counterDoubleField IS not NULL", "error": [], "warning": [] }, @@ -31561,16 +31475,6 @@ "error": [], "warning": [] }, - { - "query": "from a_index | stats var = max(datePeriodField)", - "error": [], - "warning": [] - }, - { - "query": "from a_index | stats max(datePeriodField)", - "error": [], - "warning": [] - }, { "query": "from a_index | stats var = max(booleanField)", "error": [], @@ -31654,20 +31558,6 @@ ], "warning": [] }, - { - "query": "from a_index | where max(datePeriodField)", - "error": [ - "WHERE does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | where max(datePeriodField) > 0", - "error": [ - "WHERE does not support function max" - ], - "warning": [] - }, { "query": "from a_index | where max(booleanField)", "error": [ @@ -31808,34 +31698,6 @@ ], "warning": [] }, - { - "query": "from a_index | eval var = max(datePeriodField)", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | eval var = max(datePeriodField) > 0", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | eval max(datePeriodField)", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, - { - "query": "from a_index | eval max(datePeriodField) > 0", - "error": [ - "EVAL does not support function max" - ], - "warning": [] - }, { "query": "from a_index | eval var = max(booleanField)", "error": [ @@ -32197,16 +32059,6 @@ "error": [], "warning": [] }, - { - "query": "from a_index | stats var = min(datePeriodField)", - "error": [], - "warning": [] - }, - { - "query": "from a_index | stats min(datePeriodField)", - "error": [], - "warning": [] - }, { "query": "from a_index | stats var = min(booleanField)", "error": [], @@ -32290,20 +32142,6 @@ ], "warning": [] }, - { - "query": "from a_index | where min(datePeriodField)", - "error": [ - "WHERE does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | where min(datePeriodField) > 0", - "error": [ - "WHERE does not support function min" - ], - "warning": [] - }, { "query": "from a_index | where min(booleanField)", "error": [ @@ -32444,34 +32282,6 @@ ], "warning": [] }, - { - "query": "from a_index | eval var = min(datePeriodField)", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | eval var = min(datePeriodField) > 0", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | eval min(datePeriodField)", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, - { - "query": "from a_index | eval min(datePeriodField) > 0", - "error": [ - "EVAL does not support function min" - ], - "warning": [] - }, { "query": "from a_index | eval var = min(booleanField)", "error": [ @@ -34459,20 +34269,6 @@ ], "warning": [] }, - { - "query": "from a_index | stats by bucket(dateField, datePeriodField)", - "error": [ - "Argument of [bucket] must be a constant, received [datePeriodField]" - ], - "warning": [] - }, - { - "query": "from a_index | stats by bin(dateField, datePeriodField)", - "error": [ - "Argument of [bin] must be a constant, received [datePeriodField]" - ], - "warning": [] - }, { "query": "from a_index | stats by bucket(dateField, integerField, now(), now())", "error": [ @@ -34997,28 +34793,6 @@ ], "warning": [] }, - { - "query": "from a_index | sort bucket(dateField, datePeriodField)", - "error": [ - "SORT does not support function bucket" - ], - "warning": [] - }, - { - "query": "from a_index | stats bucket(\"2022\", datePeriodField)", - "error": [ - "Argument of [bucket] must be a constant, received [datePeriodField]" - ], - "warning": [] - }, - { - "query": "from a_index | stats bucket(concat(\"20\", \"22\"), datePeriodField)", - "error": [ - "Argument of [bucket] must be [date], found value [concat(\"20\",\"22\")] type [keyword]", - "Argument of [bucket] must be a constant, received [datePeriodField]" - ], - "warning": [] - }, { "query": "from a_index | stats var = percentile(doubleField, doubleField)", "error": [ diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/types.ts b/packages/kbn-esql-validation-autocomplete/src/validation/types.ts index 41a0c9b2dfd6f..f6654540bcf86 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/types.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/types.ts @@ -7,6 +7,7 @@ */ import type { ESQLMessage, ESQLLocation } from '@kbn/esql-ast'; +import { FieldType } from '../definitions/types'; import type { EditorError } from '../types'; export interface ESQLVariable { @@ -17,10 +18,9 @@ export interface ESQLVariable { export interface ESQLRealField { name: string; - type: string; + type: FieldType; metadata?: { description?: string; - type?: string; }; } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index 80b560bd66c4f..5e61c2631d9e1 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -11,7 +11,13 @@ import { writeFile, readFile } from 'fs/promises'; import { ignoreErrorsMap, validateQuery } from './validation'; import { evalFunctionDefinitions } from '../definitions/functions'; import { getFunctionSignatures } from '../definitions/helpers'; -import { FunctionDefinition, SupportedFieldType, supportedFieldTypes } from '../definitions/types'; +import { + FieldType, + FunctionDefinition, + SupportedDataType, + dataTypes, + fieldTypes as _fieldTypes, +} from '../definitions/types'; import { timeUnits, timeUnitsToSuggest } from '../definitions/literals'; import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; import capitalize from 'lodash/capitalize'; @@ -30,6 +36,8 @@ import { import { validationFromCommandTestSuite as runFromTestSuite } from './__tests__/test_suites/validation.command.from'; import { Setup, setup } from './__tests__/helpers'; +const fieldTypes = _fieldTypes.filter((type) => type !== 'unsupported'); + const NESTING_LEVELS = 4; const NESTED_DEPTHS = Array(NESTING_LEVELS) .fill(0) @@ -76,7 +84,7 @@ function getLiteralType(typeString: 'time_literal') { return `1 ${literals[typeString]}`; } -export const fieldNameFromType = (type: SupportedFieldType) => `${camelCase(type)}Field`; +export const fieldNameFromType = (type: FieldType) => `${camelCase(type)}Field`; function getFieldName( typeString: string, @@ -127,8 +135,8 @@ function getFieldMapping( date: 'now()', }; return params.map(({ name: _name, type, constantOnly, literalOptions, ...rest }) => { - const typeString: string = type; - if (supportedFieldTypes.includes(typeString as SupportedFieldType)) { + const typeString: string = type as string; + if (dataTypes.includes(typeString as SupportedDataType)) { if (useLiterals && literalOptions) { return { name: `"${literalOptions[0]}"`, @@ -541,13 +549,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from index | keep m*', ['Unknown column [m*]']); testErrorsAndWarnings('from index | keep *m', ['Unknown column [*m]']); testErrorsAndWarnings('from index | keep d*m', ['Unknown column [d*m]']); - testErrorsAndWarnings( - 'from unsupported_index | keep unsupported_field', - [], - [ - 'Field [unsupported_field] cannot be retrieved, it is unsupported or not indexed; returning null', - ] - ); testErrorsAndWarnings( `FROM index | STATS ROUND(AVG(doubleField * 1.5)), COUNT(*), MIN(doubleField * 10) | KEEP \`MIN(doubleField * 10)\``, @@ -874,7 +875,7 @@ describe('validation logic', () => { [] ); - for (const field of supportedFieldTypes) { + for (const field of fieldTypes) { testErrorsAndWarnings(`from a_index | where ${fieldNameFromType(field)} IS NULL`, []); testErrorsAndWarnings(`from a_index | where ${fieldNameFromType(field)} IS null`, []); testErrorsAndWarnings(`from a_index | where ${fieldNameFromType(field)} is null`, []); @@ -937,7 +938,7 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval a=["a", "b"]', []); testErrorsAndWarnings('from a_index | eval a=null', []); - for (const field of supportedFieldTypes) { + for (const field of fieldTypes) { testErrorsAndWarnings(`from a_index | eval ${fieldNameFromType(field)} IS NULL`, []); testErrorsAndWarnings(`from a_index | eval ${fieldNameFromType(field)} IS null`, []); testErrorsAndWarnings(`from a_index | eval ${fieldNameFromType(field)} is null`, []); @@ -12110,8 +12111,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats var = max(dateField)', []); testErrorsAndWarnings('from a_index | stats max(dateField)', []); - testErrorsAndWarnings('from a_index | stats var = max(datePeriodField)', []); - testErrorsAndWarnings('from a_index | stats max(datePeriodField)', []); testErrorsAndWarnings('from a_index | stats var = max(booleanField)', []); testErrorsAndWarnings('from a_index | stats max(booleanField)', []); testErrorsAndWarnings('from a_index | stats var = max(ipField)', []); @@ -12153,14 +12152,6 @@ describe('validation logic', () => { 'WHERE does not support function max', ]); - testErrorsAndWarnings('from a_index | where max(datePeriodField)', [ - 'WHERE does not support function max', - ]); - - testErrorsAndWarnings('from a_index | where max(datePeriodField) > 0', [ - 'WHERE does not support function max', - ]); - testErrorsAndWarnings('from a_index | where max(booleanField)', [ 'WHERE does not support function max', ]); @@ -12240,23 +12231,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval max(dateField) > 0', [ 'EVAL does not support function max', ]); - - testErrorsAndWarnings('from a_index | eval var = max(datePeriodField)', [ - 'EVAL does not support function max', - ]); - - testErrorsAndWarnings('from a_index | eval var = max(datePeriodField) > 0', [ - 'EVAL does not support function max', - ]); - - testErrorsAndWarnings('from a_index | eval max(datePeriodField)', [ - 'EVAL does not support function max', - ]); - - testErrorsAndWarnings('from a_index | eval max(datePeriodField) > 0', [ - 'EVAL does not support function max', - ]); - testErrorsAndWarnings('from a_index | eval var = max(booleanField)', [ 'EVAL does not support function max', ]); @@ -12459,8 +12433,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats var = min(dateField)', []); testErrorsAndWarnings('from a_index | stats min(dateField)', []); - testErrorsAndWarnings('from a_index | stats var = min(datePeriodField)', []); - testErrorsAndWarnings('from a_index | stats min(datePeriodField)', []); testErrorsAndWarnings('from a_index | stats var = min(booleanField)', []); testErrorsAndWarnings('from a_index | stats min(booleanField)', []); testErrorsAndWarnings('from a_index | stats var = min(ipField)', []); @@ -12501,15 +12473,6 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | where min(dateField) > 0', [ 'WHERE does not support function min', ]); - - testErrorsAndWarnings('from a_index | where min(datePeriodField)', [ - 'WHERE does not support function min', - ]); - - testErrorsAndWarnings('from a_index | where min(datePeriodField) > 0', [ - 'WHERE does not support function min', - ]); - testErrorsAndWarnings('from a_index | where min(booleanField)', [ 'WHERE does not support function min', ]); @@ -12590,22 +12553,6 @@ describe('validation logic', () => { 'EVAL does not support function min', ]); - testErrorsAndWarnings('from a_index | eval var = min(datePeriodField)', [ - 'EVAL does not support function min', - ]); - - testErrorsAndWarnings('from a_index | eval var = min(datePeriodField) > 0', [ - 'EVAL does not support function min', - ]); - - testErrorsAndWarnings('from a_index | eval min(datePeriodField)', [ - 'EVAL does not support function min', - ]); - - testErrorsAndWarnings('from a_index | eval min(datePeriodField) > 0', [ - 'EVAL does not support function min', - ]); - testErrorsAndWarnings('from a_index | eval var = min(booleanField)', [ 'EVAL does not support function min', ]); @@ -14101,14 +14048,6 @@ describe('validation logic', () => { ] ); - testErrorsAndWarnings('from a_index | stats by bucket(dateField, datePeriodField)', [ - 'Argument of [bucket] must be a constant, received [datePeriodField]', - ]); - - testErrorsAndWarnings('from a_index | stats by bin(dateField, datePeriodField)', [ - 'Argument of [bin] must be a constant, received [datePeriodField]', - ]); - testErrorsAndWarnings( 'from a_index | stats by bucket(dateField, integerField, now(), now())', ['Argument of [bucket] must be a constant, received [integerField]'] @@ -14610,18 +14549,6 @@ describe('validation logic', () => { 'Argument of [bin] must be a constant, received [longField]', ] ); - - testErrorsAndWarnings('from a_index | sort bucket(dateField, datePeriodField)', [ - 'SORT does not support function bucket', - ]); - - testErrorsAndWarnings('from a_index | stats bucket("2022", datePeriodField)', [ - 'Argument of [bucket] must be a constant, received [datePeriodField]', - ]); - testErrorsAndWarnings('from a_index | stats bucket(concat("20", "22"), datePeriodField)', [ - 'Argument of [bucket] must be [date], found value [concat("20","22")] type [keyword]', - 'Argument of [bucket] must be a constant, received [datePeriodField]', - ]); }); describe('percentile', () => { diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts index 6cc515dc4ac74..a21c306d5d12e 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.ts @@ -111,7 +111,7 @@ function validateFunctionLiteralArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: argDef.type, + argType: argDef.type as string, value: typeof actualArg.value === 'number' ? actualArg.value : String(actualArg.value), givenType: actualArg.literalType, }, @@ -139,7 +139,7 @@ function validateFunctionLiteralArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: argDef.type, + argType: argDef.type as string, value: actualArg.name, givenType: 'duration', }, @@ -169,7 +169,7 @@ function validateInlineCastArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: parameterDefinition.type, + argType: parameterDefinition.type as string, value: arg.text, givenType: arg.castType, }, @@ -206,7 +206,7 @@ function validateNestedFunctionArg( messages.push( getMessageFromId({ messageId: 'noNestedArgumentSupport', - values: { name: actualArg.text, argType: argFn.signatures[0].returnType }, + values: { name: actualArg.text, argType: argFn.signatures[0].returnType as string }, locations: actualArg.location, }) ); @@ -219,9 +219,9 @@ function validateNestedFunctionArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: parameterDefinition.type, + argType: parameterDefinition.type as string, value: actualArg.text, - givenType: argFn.signatures[0].returnType, + givenType: argFn.signatures[0].returnType as string, }, locations: actualArg.location, }) @@ -301,7 +301,7 @@ function validateFunctionColumnArg( messageId: 'wrongArgumentType', values: { name: astFunction.name, - argType: parameterDefinition.type, + argType: parameterDefinition.type as string, value: actualArg.name, givenType: columnHit!.type, }, @@ -534,14 +534,14 @@ function validateFunction( }); }); - const shouldCollapseMessages = isArrayType(argDef.type) && hasMultipleElements; + const shouldCollapseMessages = isArrayType(argDef.type as string) && hasMultipleElements; failingSignature.push( ...(shouldCollapseMessages ? collapseWrongArgumentTypeMessages( messagesFromAllArgElements, outerArg, astFunction.name, - argDef.type, + argDef.type as string, parentCommand, references ) @@ -1076,15 +1076,17 @@ function validateUnsupportedTypeFields(fields: Map) { const messages: ESQLMessage[] = []; for (const field of fields.values()) { if (field.type === 'unsupported') { - messages.push( - getMessageFromId({ - messageId: 'unsupportedFieldType', - values: { - field: field.name, - }, - locations: { min: 1, max: 1 }, - }) - ); + // Removed temporarily to supress all these warnings + // Issue to re-enable in a better way: https://github.com/elastic/kibana/issues/189666 + // messages.push( + // getMessageFromId({ + // messageId: 'unsupportedFieldType', + // values: { + // field: field.name, + // }, + // locations: { min: 1, max: 1 }, + // }) + // ); } } return messages; diff --git a/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts b/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts index 141653c2b2cf6..d49fb150d7c12 100644 --- a/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts +++ b/packages/kbn-monaco/src/esql/lib/hover/hover.test.ts @@ -11,17 +11,21 @@ import { getHoverItem } from './hover'; import { getAstAndSyntaxErrors } from '@kbn/esql-ast'; import { ENRICH_MODES, + ESQLRealField, getFunctionDefinition, getFunctionSignatures, } from '@kbn/esql-validation-autocomplete'; +import { FieldType } from '@kbn/esql-validation-autocomplete/src/definitions/types'; -const fields: Array<{ name: string; type: string; suggestedAs?: string }> = [ - ...['string', 'number', 'date', 'boolean', 'ip'].map((type) => ({ +const types: FieldType[] = ['keyword', 'double', 'date', 'boolean', 'ip']; + +const fields: Array = [ + ...types.map((type) => ({ name: `${type}Field`, type, })), - { name: 'any#Char$Field', type: 'number', suggestedAs: '`any#Char$Field`' }, - { name: 'kubernetes.something.something', type: 'number' }, + { name: 'any#Char$Field', type: 'double', suggestedAs: '`any#Char$Field`' }, + { name: 'kubernetes.something.something', type: 'double' }, ]; const indexes = ( @@ -56,7 +60,7 @@ const policies = [ ]; function createCustomCallbackMocks( - customFields: Array<{ name: string; type: string }> | undefined, + customFields: ESQLRealField[] | undefined, customSources: Array<{ name: string; hidden: boolean }> | undefined, customPolicies: | Array<{ diff --git a/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts b/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts index 4cfebe1597607..511d14d2da3f0 100644 --- a/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts +++ b/packages/kbn-text-based-editor/src/ecs_metadata_helper.test.ts @@ -7,15 +7,15 @@ */ import { getColumnsWithMetadata } from './ecs_metadata_helper'; -import type { DatatableColumnType } from '@kbn/expressions-plugin/common'; import type { FieldsMetadataPublicStart } from '@kbn/fields-metadata-plugin/public'; +import { ESQLRealField } from '@kbn/esql-validation-autocomplete'; describe('getColumnsWithMetadata', () => { it('should return original columns if fieldsMetadata is not provided', async () => { - const columns = [ - { name: 'ecs.version', type: 'keyword' as DatatableColumnType }, - { name: 'field1', type: 'text' as DatatableColumnType }, - { name: 'field2', type: 'double' as DatatableColumnType }, + const columns: ESQLRealField[] = [ + { name: 'ecs.version', type: 'keyword' }, + { name: 'field1', type: 'text' }, + { name: 'field2', type: 'double' }, ]; const result = await getColumnsWithMetadata(columns); @@ -23,10 +23,10 @@ describe('getColumnsWithMetadata', () => { }); it('should return columns with metadata if both name and type match with ECS fields', async () => { - const columns = [ - { name: 'ecs.field', type: 'text' as DatatableColumnType }, - { name: 'ecs.fakeBooleanField', type: 'boolean' as DatatableColumnType }, - { name: 'field2', type: 'double' as DatatableColumnType }, + const columns: ESQLRealField[] = [ + { name: 'ecs.field', type: 'text' }, + { name: 'ecs.fakeBooleanField', type: 'boolean' }, + { name: 'field2', type: 'double' }, ]; const fieldsMetadata = { getClient: jest.fn().mockResolvedValue({ @@ -57,10 +57,10 @@ describe('getColumnsWithMetadata', () => { }); it('should handle keyword suffix correctly', async () => { - const columns = [ - { name: 'ecs.version', type: 'keyword' as DatatableColumnType }, - { name: 'ecs.version.keyword', type: 'keyword' as DatatableColumnType }, - { name: 'field2', type: 'double' as DatatableColumnType }, + const columns: ESQLRealField[] = [ + { name: 'ecs.version', type: 'keyword' }, + { name: 'ecs.version.keyword', type: 'keyword' }, + { name: 'field2', type: 'double' }, ]; const fieldsMetadata = { getClient: jest.fn().mockResolvedValue({ diff --git a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx index e67ffeac3c177..326dc471a86c1 100644 --- a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx +++ b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx @@ -29,6 +29,8 @@ import memoize from 'lodash/memoize'; import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { css } from '@emotion/react'; +import { ESQLRealField } from '@kbn/esql-validation-autocomplete'; +import { FieldType } from '@kbn/esql-validation-autocomplete/src/definitions/types'; import { EditorFooter } from './editor_footer'; import { fetchFieldsFromESQL } from './fetch_fields_from_esql'; import { @@ -322,13 +324,12 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ undefined, abortController ).result; - const columns = + const columns: ESQLRealField[] = table?.columns.map((c) => { - // Casting unsupported as unknown to avoid plethora of warnings - // Remove when addressed https://github.com/elastic/kibana/issues/189666 - if (!c.meta.esType || c.meta.esType === 'unsupported') - return { name: c.name, type: 'unknown' }; - return { name: c.name, type: c.meta.esType }; + return { + name: c.name, + type: c.meta.esType as FieldType, + }; }) || []; return await getRateLimitedColumnsWithMetadata(columns, fieldsMetadata); } catch (e) { diff --git a/test/api_integration/apis/esql/errors.ts b/test/api_integration/apis/esql/errors.ts index 193d48c478209..bf2d7e2139a91 100644 --- a/test/api_integration/apis/esql/errors.ts +++ b/test/api_integration/apis/esql/errors.ts @@ -189,7 +189,12 @@ export default function ({ getService }: FtrProviderContext) { const fieldsExcludingCounterType = config.fields.filter( // ES|QL supports counter_integer, counter_long, counter_double, date_period, etc. // but they are not types suitable for Elasticsearch indices - (c: { type: string }) => !c.type.startsWith('counter_') && c.type !== 'date_period' + (c: { type: string }) => + !c.type.startsWith('counter_') && + c.type !== 'date_period' && + c.type !== 'time_duration' && + c.type !== 'null' && + c.type !== 'time_literal' ); await es.indices.create( createIndexRequest(