Skip to content

Commit

Permalink
get policies and modes working
Browse files Browse the repository at this point in the history
  • Loading branch information
drewdaemon committed Feb 20, 2025
1 parent 9b10195 commit 867924a
Show file tree
Hide file tree
Showing 10 changed files with 32 additions and 130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('autocomplete.suggest', () => {
modes.map((mode) => `_${mode}:$0`),
{ triggerCharacter: '_' }
);
await assertSuggestions('from a | enrich _any: /', []);
for (const mode of modes) {
await assertSuggestions(`from a | enrich _${mode}:/`, expectedPolicyNameSuggestions, {
triggerCharacter: ':',
Expand Down Expand Up @@ -72,24 +73,27 @@ describe('autocomplete.suggest', () => {
'var0 = ',
...getPolicyFields('policy'),
]);
assertSuggestions(`from a | enrich policy on b with var0 /`, ['= $0', ',', '| ']);
assertSuggestions(`from a | enrich policy on b with var0 = /`, [
await assertSuggestions(`from a | enrich policy on b with var0 /`, ['= $0', ',', '| ']);
await assertSuggestions(`from a | enrich policy on b with var0 = /`, [
...getPolicyFields('policy'),
]);
assertSuggestions(`from a | enrich policy on b with var0 = keywordField /`, [',', '| ']);
assertSuggestions(`from a | enrich policy on b with var0 = keywordField, /`, [
await assertSuggestions(`from a | enrich policy on b with var0 = keywordField /`, [
',',
'| ',
]);
await assertSuggestions(`from a | enrich policy on b with var0 = keywordField, /`, [
'var1 = ',
...getPolicyFields('policy'),
]);
assertSuggestions(`from a | enrich policy on b with var0 = keywordField, var1 /`, [
await assertSuggestions(`from a | enrich policy on b with var0 = keywordField, var1 /`, [
'= $0',
',',
'| ',
]);
assertSuggestions(`from a | enrich policy on b with var0 = keywordField, var1 = /`, [
await assertSuggestions(`from a | enrich policy on b with var0 = keywordField, var1 = /`, [
...getPolicyFields('policy'),
]);
assertSuggestions(
await assertSuggestions(
`from a | enrich policy with /`,
['var0 = ', ...getPolicyFields('policy')],
{ triggerCharacter: ' ' }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
getCommandDefinition,
getCommandOption,
getFunctionDefinition,
getLastNonWhitespaceChar,
isAssignment,
isAssignmentComplete,
isColumnItem,
Expand All @@ -33,7 +32,6 @@ import {
isOptionItem,
isRestartingExpression,
isSourceCommand,
isSettingItem,
isSourceItem,
isTimeIntervalItem,
getAllFunctions,
Expand Down Expand Up @@ -61,14 +59,12 @@ import {
buildFieldsDefinitions,
buildPoliciesDefinitions,
getNewVariableSuggestion,
buildNoPoliciesAvailableDefinition,
getFunctionSuggestions,
buildMatchingFieldsDefinition,
getCompatibleLiterals,
buildConstantsDefinitions,
buildVariablesDefinitions,
buildOptionDefinition,
buildSettingDefinitions,
buildValueDefinitions,
getDateLiterals,
buildFieldsDefinitionsWithMetadata,
Expand Down Expand Up @@ -225,16 +221,6 @@ export async function suggest(
supportsControls
);
}
if (astContext.type === 'setting') {
return getSettingArgsSuggestions(
innerText,
ast,
astContext,
getFieldsByType,
getFieldsMap,
getPolicyMetadata
);
}
if (astContext.type === 'option') {
// need this wrap/unwrap thing to make TS happy
const { option, ...rest } = astContext;
Expand Down Expand Up @@ -427,6 +413,7 @@ async function getSuggestionsWithinCommandExpression(
callbacks,
getVariablesByType,
supportsControls,
getPolicies,
});
} else {
// The deprecated path.
Expand Down Expand Up @@ -870,29 +857,10 @@ async function getExpressionSuggestionsByType(
}
}
}
if (argDef.type === 'source') {
if (argDef.innerTypes?.includes('policy')) {
// ... | ENRICH <suggest>
const policies = await getPolicies();
const lastWord = findFinalWord(innerText);
if (lastWord !== '') {
policies.forEach((suggestion) => {
suggestions.push({
...suggestion,
rangeToReplace: {
start: innerText.length - lastWord.length + 1,
end: innerText.length + 1,
},
});
});
}
suggestions.push(...(policies.length ? policies : [buildNoPoliciesAvailableDefinition()]));
}
}
}

const nonOptionArgs = command.args.filter(
(arg) => !isOptionItem(arg) && !isSettingItem(arg) && !Array.isArray(arg) && !arg.incomplete
(arg) => !isOptionItem(arg) && !Array.isArray(arg) && !arg.incomplete
);
// Perform some checks on mandatory arguments
const mandatoryArgsAlreadyPresent =
Expand Down Expand Up @@ -1236,35 +1204,6 @@ async function getListArgsSuggestions(
return suggestions;
}

async function getSettingArgsSuggestions(
innerText: string,
commands: ESQLCommand[],
{
command,
node,
}: {
command: ESQLCommand;
node: ESQLSingleAstItem | undefined;
},
getFieldsByType: GetColumnsByTypeFn,
getFieldsMaps: GetFieldsMapFn,
getPolicyMetadata: GetPolicyMetadataFn
) {
const suggestions = [];

const settingDefs = getCommandDefinition(command.name).modes || [];

if (settingDefs.length) {
const lastChar = getLastNonWhitespaceChar(innerText);
const matchingSettingDefs = settingDefs.filter(({ prefix }) => lastChar === prefix);
if (matchingSettingDefs.length) {
// COMMAND _<here>
suggestions.push(...matchingSettingDefs.flatMap(buildSettingDefinitions));
}
}
return suggestions;
}

/**
* @deprecated — this will disappear when https://github.com/elastic/kibana/issues/195418 is complete
* because "options" will be handled in imperative command-specific routines instead of being independent.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ export const suggest: CommandBaseDefinition<'join'>['suggest'] = async ({
getColumnsByType,
definition,
callbacks,
previousCommands,
}: CommandSuggestParams<'join'>): Promise<SuggestionRawDefinition[]> => {
let commandText: string = innerText;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { timeUnitsToSuggest } from '../definitions/literals';
import {
FunctionDefinition,
CommandOptionsDefinition,
CommandModeDefinition,
FunctionParameterType,
} from '../definitions/types';
import { shouldBeQuotedSource, shouldBeQuotedText } from '../shared/helpers';
Expand Down Expand Up @@ -399,42 +398,6 @@ export const buildOptionDefinition = (
return completeItem;
};

export const buildSettingDefinitions = (
setting: CommandModeDefinition
): SuggestionRawDefinition[] => {
// for now there's just a single setting with one argument
return setting.values.map(({ name, description }) => ({
label: `${setting.prefix || ''}${name}`,
text: `${setting.prefix || ''}${name}:$0`,
asSnippet: true,
kind: 'Reference',
detail: description ? `${setting.description} - ${description}` : setting.description,
sortText: 'D',
command: TRIGGER_SUGGESTION_COMMAND,
}));
};

export const buildNoPoliciesAvailableDefinition = (): SuggestionRawDefinition => ({
label: i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.noPoliciesLabel', {
defaultMessage: 'No available policy',
}),
text: '',
kind: 'Issue',
detail: i18n.translate(
'kbn-esql-validation-autocomplete.esql.autocomplete.noPoliciesLabelsFound',
{
defaultMessage: 'Click to create',
}
),
sortText: 'D',
command: {
id: 'esql.policies.create',
title: i18n.translate('kbn-esql-validation-autocomplete.esql.autocomplete.createNewPolicy', {
defaultMessage: 'Click to create',
}),
},
});

export function getUnitDuration(unit: number = 1) {
const filteredTimeLiteral = timeUnitsToSuggest.filter(({ name }) => {
const result = /s$/.test(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
isFunctionOperatorParam,
isLiteralItem,
} from '../shared/helpers';
import { ENRICH_MODES } from './settings';
import {
appendSeparatorOption,
asOption,
Expand All @@ -36,6 +35,8 @@ import {
onOption,
withOption,
} from './options';
import { ENRICH_MODES } from './settings';

import type { CommandDefinition } from './types';
import { suggest as suggestForSort } from '../autocomplete/commands/sort';
import { suggest as suggestForKeep } from '../autocomplete/commands/keep';
Expand All @@ -48,6 +49,7 @@ import { suggest as suggestForRow } from '../autocomplete/commands/row';
import { suggest as suggestForShow } from '../autocomplete/commands/show';
import { suggest as suggestForGrok } from '../autocomplete/commands/grok';
import { suggest as suggestForDissect } from '../autocomplete/commands/dissect';
import { suggest as suggestForEnrich } from '../autocomplete/commands/enrich';

const statsValidator = (command: ESQLCommand) => {
const messages: ESQLMessage[] = [];
Expand Down Expand Up @@ -517,14 +519,15 @@ export const commandDefinitions: Array<CommandDefinition<any>> = [
multipleParams: false,
params: [{ name: 'policyName', type: 'source', innerTypes: ['policy'] }],
},
suggest: suggestForEnrich,
},
{
name: 'hidden_command',
description: 'A test fixture to test hidden-ness',
hidden: true,
examples: [],
modes: [],
options: [],
modes: [],
signature: {
params: [],
multipleParams: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { i18n } from '@kbn/i18n';
import type { CommandModeDefinition } from './types';

/** @deprecated — the concept of a "mode" will go away soon */
export const ENRICH_MODES: CommandModeDefinition = {
name: 'ccq.mode',
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.ccqModeDoc', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ export interface CommandSuggestParams<CommandName extends string> {
* @returns
*/
getSources: () => Promise<ESQLSourceResult[]>;
/**
* Fetch suggestions for all available policies
*/
getPolicies: () => Promise<SuggestionRawDefinition[]>;
/**
* Inspect the AST and returns the sources that are used in the query.
* @param type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@ import {
Walker,
isIdentifier,
} from '@kbn/esql-ast';
import { ENRICH_MODES } from '../definitions/settings';
import { EDITOR_MARKER } from './constants';
import {
isOptionItem,
isColumnItem,
isSourceItem,
isSettingItem,
pipePrecedesCurrentWord,
getFunctionDefinition,
} from './helpers';
Expand Down Expand Up @@ -65,10 +63,6 @@ function findOption(nodes: ESQLAstItem[], offset: number): ESQLCommandOption | u
return findCommandSubType(nodes, offset, isOptionItem);
}

function findSetting(nodes: ESQLAstItem[], offset: number): ESQLCommandMode | undefined {
return findCommandSubType(nodes, offset, isSettingItem);
}

function findCommandSubType<T extends ESQLCommandMode | ESQLCommandOption>(
nodes: ESQLAstItem[],
offset: number,
Expand Down Expand Up @@ -130,7 +124,6 @@ function findAstPosition(ast: ESQLAst, offset: number) {
command: removeMarkerArgFromArgsList(command)!,
option: removeMarkerArgFromArgsList(findOption(command.args, offset)),
node: removeMarkerArgFromArgsList(cleanMarkerNode(findNode(command.args, offset))),
setting: removeMarkerArgFromArgsList(findSetting(command.args, offset)),
};
}

Expand Down Expand Up @@ -170,16 +163,16 @@ export function getAstContext(queryString: string, ast: ESQLAst, offset: number)
};
}

const { command, option, setting, node } = findAstPosition(ast, offset);
const { command, option, node } = findAstPosition(ast, offset);
if (node) {
if (node.type === 'literal' && node.literalType === 'keyword') {
// command ... "<here>"
return { type: 'value' as const, command, node, option, setting };
return { type: 'value' as const, command, node, option };
}
if (node.type === 'function') {
if (['in', 'not_in'].includes(node.name) && Array.isArray(node.args[1])) {
// command ... a in ( <here> )
return { type: 'list' as const, command, node, option, setting };
return { type: 'list' as const, command, node, option };
}
if (
isNotEnrichClauseAssigment(node, command) &&
Expand All @@ -190,24 +183,19 @@ export function getAstContext(queryString: string, ast: ESQLAst, offset: number)
!(isBuiltinFunction(node) && command.name !== 'stats')
) {
// command ... fn( <here> )
return { type: 'function' as const, command, node, option, setting };
return { type: 'function' as const, command, node, option };
}
}
// for now it's only an enrich thing
if (node.type === 'source' && node.text === ENRICH_MODES.prefix) {
// command _<here>
return { type: 'setting' as const, command, node, option, setting };
}
}
if (!command || (queryString.length <= offset && pipePrecedesCurrentWord(queryString))) {
// // ... | <here>
return { type: 'newCommand' as const, command: undefined, node, option, setting };
return { type: 'newCommand' as const, command: undefined, node, option };
}

// TODO — remove this option branch once https://github.com/elastic/kibana/issues/195418 is complete
if (command && isOptionItem(command.args[command.args.length - 1]) && command.name !== 'stats') {
if (option) {
return { type: 'option' as const, command, node, option, setting };
return { type: 'option' as const, command, node, option };
}
}

Expand All @@ -217,6 +205,5 @@ export function getAstContext(queryString: string, ast: ESQLAst, offset: number)
command,
option,
node,
setting,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
Walker,
type ESQLAstItem,
type ESQLColumn,
type ESQLCommandMode,
type ESQLCommandOption,
type ESQLFunction,
type ESQLLiteral,
Expand All @@ -20,6 +19,7 @@ import {
type ESQLTimeInterval,
} from '@kbn/esql-ast';
import {
ESQLCommandMode,
ESQLIdentifier,
ESQLInlineCast,
ESQLParamLiteral,
Expand Down Expand Up @@ -65,6 +65,7 @@ export function isSingleItem(arg: ESQLAstItem): arg is ESQLSingleAstItem {
return arg && !Array.isArray(arg);
}

/** @deprecated — a "setting" is a concept we will be getting rid of soon */
export function isSettingItem(arg: ESQLAstItem): arg is ESQLCommandMode {
return isSingleItem(arg) && arg.type === 'mode';
}
Expand Down
Loading

0 comments on commit 867924a

Please sign in to comment.