Skip to content

Commit

Permalink
[ES|QL] Separate ENRICH autocomplete routine (#211657)
Browse files Browse the repository at this point in the history
## Summary

Part of #195418

Gives `ENRICH` autocomplete logic its own home 🏡

### 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

### Identify risks

- [ ] As with any refactor, there's a possibility this will introduce a
regression in the behavior of commands. However, all automated tests are
passing and I have tested the behavior manually and can detect no
regression.

---------

Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
  • Loading branch information
drewdaemon and stratoula authored Mar 3, 2025
1 parent d291339 commit f2a9173
Show file tree
Hide file tree
Showing 13 changed files with 549 additions and 376 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { camelCase } from 'lodash';
import { getFieldNamesByType, getPolicyFields, policies, setup } from './helpers';

describe('autocomplete.suggest', () => {
describe('ENRICH', () => {
const modes = ['any', 'coordinator', 'remote'];
const expectedPolicyNameSuggestions = policies
.map(({ name, suggestedAs }) => suggestedAs || name)
.map((name) => `${name} `);

let assertSuggestions: Awaited<ReturnType<typeof setup>>['assertSuggestions'];
beforeEach(async () => {
const setupResult = await setup();
assertSuggestions = setupResult.assertSuggestions;
});

it('suggests policy names', async () => {
await assertSuggestions(`from a | enrich /`, expectedPolicyNameSuggestions);
await assertSuggestions(`from a | enrich po/`, expectedPolicyNameSuggestions);
});

test('modes', async () => {
await assertSuggestions(
`from a | enrich _/`,
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: ':',
});

await assertSuggestions(
`from a | enrich _${mode.toUpperCase()}:/`,
expectedPolicyNameSuggestions,
{ triggerCharacter: ':' }
);

await assertSuggestions(
`from a | enrich _${camelCase(mode)}:/`,
expectedPolicyNameSuggestions,
{ triggerCharacter: ':' }
);
}
});

it('suggests ON and WITH after policy name', async () => {
await assertSuggestions(`from a | enrich policy /`, ['ON ', 'WITH ', '| ']);
await assertSuggestions(`from a | enrich policy O/`, ['ON ', 'WITH ', '| ']);
});

it('suggests fields after ON', async () => {
await assertSuggestions(
`from a | enrich policy on /`,
getFieldNamesByType('any').map((v) => `${v} `)
);
await assertSuggestions(
`from a | enrich policy on fi/`,
getFieldNamesByType('any').map((v) => `${v} `)
);
});

describe('WITH', () => {
it('suggests WITH after ON <field>', async () => {
await assertSuggestions(`from a | enrich policy on field /`, ['WITH ', '| ']);
});

it('suggests fields for new WITH clauses', async () => {
await assertSuggestions(`from a | enrich policy on field with /`, [
'var0 = ',
...getPolicyFields('policy').map((name) => ({
text: name,
// Makes sure the suggestion menu isn't opened when a field is accepted
command: undefined,
})),
]);
await assertSuggestions(`from a | enrich policy on field with fi/`, [
'var0 = ',
...getPolicyFields('policy'),
]);
await assertSuggestions(`from a | enrich policy on b with var0 = otherField, /`, [
'var1 = ',
...getPolicyFields('policy'),
]);
await assertSuggestions(`from a | enrich policy on b with var0 = otherField, fi/`, [
'var1 = ',
...getPolicyFields('policy'),
]);
});

test('waits to suggest fields until space', async () => {
await assertSuggestions(`from a | enrich policy on b with var0 = otherField,/`, []);
await assertSuggestions(`from a | enrich policy on b with/`, []);
});

test('after first word', async () => {
// not a recognized column name
await assertSuggestions(`from a | enrich policy on b with var0 /`, ['= $0']);
// recognized column name
await assertSuggestions(`from a | enrich policy on b with otherField /`, [',', '| ']);
});

test('suggests enrich fields after open assignment', async () => {
await assertSuggestions(`from a | enrich policy on b with var0 = /`, [
...getPolicyFields('policy'),
]);
await assertSuggestions(`from a | enrich policy on b with var0 = fi/`, [
...getPolicyFields('policy'),
]);
await assertSuggestions(`from a | enrich policy on b with var0 = otherField, var1 = /`, [
...getPolicyFields('policy'),
]);
});

test('after complete clause', async () => {
// works with escaped field names
await assertSuggestions(`from a | enrich policy on b with var0 = \`otherField\` /`, [
',',
'| ',
]);
await assertSuggestions(`from a | enrich policy on b with var0=otherField /`, [',', '| ']);
await assertSuggestions(`from a | enrich policy on b with otherField /`, [',', '| ']);
});

test('after user-defined column name', async () => {
await assertSuggestions(`from a | enrich policy on b with var0 = otherField, var1 /`, [
'= $0',
]);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { scalarFunctionDefinitions } from '../definitions/generated/scalar_funct
import { timeUnitsToSuggest } from '../definitions/literals';
import { commandDefinitions as unmodifiedCommandDefinitions } from '../definitions/commands';
import { getSafeInsertText, TIME_SYSTEM_PARAMS, TRIGGER_SUGGESTION_COMMAND } from './factories';
import { camelCase } from 'lodash';
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
import {
policies,
Expand Down Expand Up @@ -206,84 +205,6 @@ describe('autocomplete', () => {
});
}

describe('enrich', () => {
const modes = ['any', 'coordinator', 'remote'];
const expectedPolicyNameSuggestions = policies
.map(({ name, suggestedAs }) => suggestedAs || name)
.map((name) => `${name} `);
for (const prevCommand of [
'',
// '| enrich other-policy ',
// '| enrich other-policy on b ',
// '| enrich other-policy with c ',
]) {
testSuggestions(`from a ${prevCommand}| enrich /`, expectedPolicyNameSuggestions);
testSuggestions(
`from a ${prevCommand}| enrich _/`,
modes.map((mode) => `_${mode}:$0`),
'_'
);
for (const mode of modes) {
testSuggestions(
`from a ${prevCommand}| enrich _${mode}:/`,
expectedPolicyNameSuggestions,
':'
);
testSuggestions(
`from a ${prevCommand}| enrich _${mode.toUpperCase()}:/`,
expectedPolicyNameSuggestions,
':'
);
testSuggestions(
`from a ${prevCommand}| enrich _${camelCase(mode)}:/`,
expectedPolicyNameSuggestions,
':'
);
}
testSuggestions(`from a ${prevCommand}| enrich policy /`, ['ON $0', 'WITH $0', '| ']);
testSuggestions(
`from a ${prevCommand}| enrich policy on /`,
getFieldNamesByType('any').map((v) => `${v} `)
);
testSuggestions(`from a ${prevCommand}| enrich policy on b /`, ['WITH $0', '| ']);
testSuggestions(
`from a ${prevCommand}| enrich policy on b with /`,
['var0 = ', ...getPolicyFields('policy')],
' '
);
testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 /`, ['= $0', ',', '| ']);
testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = /`, [
...getPolicyFields('policy'),
]);
testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = keywordField /`, [
',',
'| ',
]);
testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = keywordField, /`, [
'var1 = ',
...getPolicyFields('policy'),
]);
testSuggestions(
`from a ${prevCommand}| enrich policy on b with var0 = keywordField, var1 /`,
['= $0', ',', '| ']
);
testSuggestions(
`from a ${prevCommand}| enrich policy on b with var0 = keywordField, var1 = /`,
[...getPolicyFields('policy')]
);
testSuggestions(
`from a ${prevCommand}| enrich policy with /`,
['var0 = ', ...getPolicyFields('policy')],
' '
);
testSuggestions(`from a ${prevCommand}| enrich policy with keywordField /`, [
'= $0',
',',
'| ',
]);
}
});

// @TODO: get updated eval block from main
describe('values suggestions', () => {
testSuggestions('FROM "i/"', []);
Expand Down Expand Up @@ -449,7 +370,7 @@ describe('autocomplete', () => {
);

// ENRICH policy ON
testSuggestions('FROM index1 | ENRICH policy O/', ['ON $0', 'WITH $0', '| ']);
testSuggestions('FROM index1 | ENRICH policy O/', ['ON ', 'WITH ', '| ']);

// ENRICH policy ON field
testSuggestions(
Expand Down Expand Up @@ -816,10 +737,7 @@ describe('autocomplete', () => {
.map(attachTriggerCommand)
.map((s) => ({ ...s, rangeToReplace: { start: 17, end: 20 } }))
);
testSuggestions(
'FROM a | ENRICH policy /',
['ON $0', 'WITH $0', '| '].map(attachTriggerCommand)
);
testSuggestions('FROM a | ENRICH policy /', ['ON ', 'WITH ', '| '].map(attachTriggerCommand));

testSuggestions(
'FROM a | ENRICH policy ON /',
Expand All @@ -829,12 +747,12 @@ describe('autocomplete', () => {
);
testSuggestions(
'FROM a | ENRICH policy ON @timestamp /',
['WITH $0', '| '].map(attachTriggerCommand)
['WITH ', '| '].map(attachTriggerCommand)
);
// nothing fancy with this field list
testSuggestions('FROM a | ENRICH policy ON @timestamp WITH /', [
'var0 = ',
...getPolicyFields('policy').map((name) => ({ text: name, command: undefined })),
...getPolicyFields('policy'),
]);
describe('replacement range', () => {
testSuggestions('FROM a | ENRICH policy ON @timestamp WITH othe/', [
Expand Down
Loading

0 comments on commit f2a9173

Please sign in to comment.