mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[ES|QL] Dashboard variables follow ups (#208338)](https://github.com/elastic/kibana/pull/208338) <!--- Backport version: 9.6.4 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Stratoula Kalafateli","email":"efstratia.kalafateli@elastic.co"},"sourceCommit":{"committedDate":"2025-01-28T12:39:52Z","message":"[ES|QL] Dashboard variables follow ups (#208338)","sha":"fa7c477ff19855d32f5d8dd348a6b0ce50381c39","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Feature:ES|QL","Team:ESQL","backport:version","v8.18.0"],"title":"[ES|QL] Dashboard variables follow ups","number":208338,"url":"https://github.com/elastic/kibana/pull/208338","mergeCommit":{"message":"[ES|QL] Dashboard variables follow ups (#208338)","sha":"fa7c477ff19855d32f5d8dd348a6b0ce50381c39"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/208338","number":208338,"mergeCommit":{"message":"[ES|QL] Dashboard variables follow ups (#208338)","sha":"fa7c477ff19855d32f5d8dd348a6b0ce50381c39"}},{"branch":"8.x","label":"v8.18.0","branchLabelMappingKey":"^v8.18.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT-->
This commit is contained in:
parent
64fc272dad
commit
580bb5a41d
16 changed files with 98 additions and 83 deletions
|
@ -38,9 +38,12 @@ 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, ESQLControlVariable } from '@kbn/esql-validation-autocomplete';
|
||||
import {
|
||||
type ESQLRealField,
|
||||
ESQLVariableType,
|
||||
type ESQLControlVariable,
|
||||
} from '@kbn/esql-validation-autocomplete';
|
||||
import { FieldType } from '@kbn/esql-validation-autocomplete/src/definitions/types';
|
||||
import { ESQLVariableType } from '@kbn/esql-validation-autocomplete';
|
||||
import { EditorFooter } from './editor_footer';
|
||||
import { fetchFieldsFromESQL } from './fetch_fields_from_esql';
|
||||
import {
|
||||
|
|
|
@ -18,6 +18,7 @@ export {
|
|||
getESQLWithSafeLimit,
|
||||
appendToESQLQuery,
|
||||
appendWhereClauseToESQLQuery,
|
||||
appendStatsByToQuery,
|
||||
getESQLQueryColumns,
|
||||
getESQLQueryColumnsRaw,
|
||||
getESQLResults,
|
||||
|
@ -36,6 +37,7 @@ export {
|
|||
TextBasedLanguages,
|
||||
queryCannotBeSampled,
|
||||
mapVariableToColumn,
|
||||
getValuesFromQueryField,
|
||||
} from './src';
|
||||
|
||||
export { ENABLE_ESQL, FEEDBACK_LINK } from './constants';
|
||||
|
|
|
@ -22,9 +22,14 @@ export {
|
|||
retrieveMetadataColumns,
|
||||
getQueryColumnsFromESQLQuery,
|
||||
mapVariableToColumn,
|
||||
getValuesFromQueryField,
|
||||
} from './utils/query_parsing_helpers';
|
||||
export { queryCannotBeSampled } from './utils/query_cannot_be_sampled';
|
||||
export { appendToESQLQuery, appendWhereClauseToESQLQuery } from './utils/append_to_query';
|
||||
export {
|
||||
appendToESQLQuery,
|
||||
appendWhereClauseToESQLQuery,
|
||||
appendStatsByToQuery,
|
||||
} from './utils/append_to_query';
|
||||
export {
|
||||
getESQLQueryColumns,
|
||||
getESQLQueryColumnsRaw,
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { appendToESQLQuery, appendWhereClauseToESQLQuery } from './append_to_query';
|
||||
import {
|
||||
appendToESQLQuery,
|
||||
appendWhereClauseToESQLQuery,
|
||||
appendStatsByToQuery,
|
||||
} from './append_to_query';
|
||||
|
||||
describe('appendToQuery', () => {
|
||||
describe('appendToESQLQuery', () => {
|
||||
|
@ -175,4 +179,20 @@ and \`ip\`::string!="127.0.0.2/32"`
|
|||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('appendStatsByToQuery', () => {
|
||||
it('should append the stats by clause to the query', () => {
|
||||
const queryString = 'FROM my_index';
|
||||
const statsBy = 'my_field';
|
||||
const updatedQueryString = appendStatsByToQuery(queryString, statsBy);
|
||||
expect(updatedQueryString).toBe('FROM my_index\n| STATS BY my_field');
|
||||
});
|
||||
|
||||
it('should append the stats by clause to the query with existing clauses', () => {
|
||||
const queryString = 'FROM my_index | LIMIT 10 | STATS BY meow';
|
||||
const statsBy = 'my_field';
|
||||
const updatedQueryString = appendStatsByToQuery(queryString, statsBy);
|
||||
expect(updatedQueryString).toBe('FROM my_index | LIMIT 10\n| STATS BY my_field');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse, mutate, BasicPrettyPrinter } from '@kbn/esql-ast';
|
||||
|
||||
// Append in a new line the appended text to take care of the case where the user adds a comment at the end of the query
|
||||
// in these cases a base query such as "from index // comment" will result in errors or wrong data if we don't append in a new line
|
||||
|
@ -98,3 +99,16 @@ export function appendWhereClauseToESQLQuery(
|
|||
const whereClause = `| WHERE ${fieldName}${operator}${filterValue}`;
|
||||
return appendToESQLQuery(baseESQLQuery, whereClause);
|
||||
}
|
||||
|
||||
export const appendStatsByToQuery = (queryString: string, column: string) => {
|
||||
const { root } = parse(queryString);
|
||||
const lastCommand = root.commands[root.commands.length - 1];
|
||||
if (lastCommand.name === 'stats') {
|
||||
const statsCommand = lastCommand;
|
||||
mutate.generic.commands.remove(root, statsCommand);
|
||||
const queryWithoutStats = BasicPrettyPrinter.print(root);
|
||||
return `${queryWithoutStats}\n| STATS BY ${column}`;
|
||||
} else {
|
||||
return `${queryString}\n| STATS BY ${column}`;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
retrieveMetadataColumns,
|
||||
getQueryColumnsFromESQLQuery,
|
||||
mapVariableToColumn,
|
||||
getValuesFromQueryField,
|
||||
} from './query_parsing_helpers';
|
||||
|
||||
describe('esql query helpers', () => {
|
||||
|
@ -539,4 +540,18 @@ describe('esql query helpers', () => {
|
|||
expect(mapVariableToColumn(esql, variables, columns)).toStrictEqual(expectedColumns);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getValuesFromQueryField', () => {
|
||||
it('should return the values from the query field', () => {
|
||||
const queryString = 'FROM my_index | WHERE my_field ==';
|
||||
const values = getValuesFromQueryField(queryString);
|
||||
expect(values).toEqual('my_field');
|
||||
});
|
||||
|
||||
it('should return the values from the query field with new lines', () => {
|
||||
const queryString = 'FROM my_index \n| WHERE my_field >=';
|
||||
const values = getValuesFromQueryField(queryString);
|
||||
expect(values).toEqual('my_field');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -180,3 +180,20 @@ export const mapVariableToColumn = (
|
|||
});
|
||||
return columns;
|
||||
};
|
||||
|
||||
export const getValuesFromQueryField = (queryString: string) => {
|
||||
const validQuery = `${queryString} ""`;
|
||||
const { root } = parse(validQuery);
|
||||
const lastCommand = root.commands[root.commands.length - 1];
|
||||
const columns: ESQLColumn[] = [];
|
||||
|
||||
walk(lastCommand, {
|
||||
visitColumn: (node) => columns.push(node),
|
||||
});
|
||||
|
||||
const column = Walker.match(lastCommand, { type: 'column' });
|
||||
|
||||
if (column) {
|
||||
return `${column.name}`;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
export type { SuggestionRawDefinition, ItemKind } from './src/autocomplete/types';
|
||||
export { ESQLVariableType, type ESQLControlVariable } from './src/shared/types';
|
||||
export { inKnownTimeInterval } from './src/shared/helpers';
|
||||
export type { CodeAction } from './src/code_actions/types';
|
||||
export type {
|
||||
FunctionDefinition,
|
||||
|
@ -68,6 +67,7 @@ export {
|
|||
isSingleItem,
|
||||
} from './src/shared/helpers';
|
||||
export { ENRICH_MODES } from './src/definitions/settings';
|
||||
export { timeUnits } from './src/definitions/literals';
|
||||
export { getFunctionSignatures } from './src/definitions/helpers';
|
||||
|
||||
export {
|
||||
|
|
|
@ -26,10 +26,9 @@ import { buildFunctionDocumentation } from './documentation_util';
|
|||
import { DOUBLE_BACKTICK, SINGLE_TICK_REGEX } from '../shared/constants';
|
||||
import { ESQLRealField } from '../validation/types';
|
||||
import { isNumericType } from '../shared/esql_types';
|
||||
import type { ESQLControlVariable } from '../shared/types';
|
||||
import { getTestFunctions } from '../shared/test_functions';
|
||||
import { builtinFunctions } from '../definitions/builtin';
|
||||
import { ESQLVariableType } from '../shared/types';
|
||||
import { ESQLVariableType, ESQLControlVariable } from '../shared/types';
|
||||
|
||||
const techPreviewLabel = i18n.translate(
|
||||
'kbn-esql-validation-autocomplete.esql.autocomplete.techPreviewLabel',
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
* 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 { ESQLControlVariable } from '@kbn/esql-validation-autocomplete';
|
||||
import type { ESQLControlVariable } from '@kbn/esql-validation-autocomplete';
|
||||
import { PublishingSubject } from '@kbn/presentation-publishing';
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"target/**/*",
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/esql-validation-autocomplete",
|
||||
"@kbn/presentation-publishing",
|
||||
"@kbn/esql-validation-autocomplete",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ import {
|
|||
getQueryForFields,
|
||||
areValuesIntervalsValid,
|
||||
getRecurrentVariableName,
|
||||
getValuesFromQueryField,
|
||||
appendStatsByToQuery,
|
||||
validateVariableName,
|
||||
} from './helpers';
|
||||
|
||||
|
@ -90,36 +88,6 @@ describe('helpers', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getValuesFromQueryField', () => {
|
||||
it('should return the values from the query field', () => {
|
||||
const queryString = 'FROM my_index | WHERE my_field ==';
|
||||
const values = getValuesFromQueryField(queryString);
|
||||
expect(values).toEqual('my_field');
|
||||
});
|
||||
|
||||
it('should return the values from the query field with new lines', () => {
|
||||
const queryString = 'FROM my_index \n| WHERE my_field >=';
|
||||
const values = getValuesFromQueryField(queryString);
|
||||
expect(values).toEqual('my_field');
|
||||
});
|
||||
});
|
||||
|
||||
describe('appendStatsByToQuery', () => {
|
||||
it('should append the stats by clause to the query', () => {
|
||||
const queryString = 'FROM my_index';
|
||||
const statsBy = 'my_field';
|
||||
const updatedQueryString = appendStatsByToQuery(queryString, statsBy);
|
||||
expect(updatedQueryString).toBe('FROM my_index\n| STATS BY my_field');
|
||||
});
|
||||
|
||||
it('should append the stats by clause to the query with existing clauses', () => {
|
||||
const queryString = 'FROM my_index | LIMIT 10 | STATS BY meow';
|
||||
const statsBy = 'my_field';
|
||||
const updatedQueryString = appendStatsByToQuery(queryString, statsBy);
|
||||
expect(updatedQueryString).toBe('FROM my_index | LIMIT 10\n| STATS BY my_field');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateVariableName', () => {
|
||||
it('should return the variable without special characters', () => {
|
||||
const variable = validateVariableName('my_variable/123');
|
||||
|
|
|
@ -7,8 +7,11 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
import { monaco } from '@kbn/monaco';
|
||||
import { inKnownTimeInterval } from '@kbn/esql-validation-autocomplete';
|
||||
import { type ESQLColumn, parse, walk, mutate, BasicPrettyPrinter, Walker } from '@kbn/esql-ast';
|
||||
import { timeUnits } from '@kbn/esql-validation-autocomplete';
|
||||
|
||||
function inKnownTimeInterval(timeIntervalUnit: string): boolean {
|
||||
return timeUnits.some((unit) => unit === timeIntervalUnit.toLowerCase());
|
||||
}
|
||||
|
||||
export const updateQueryStringWithVariable = (
|
||||
queryString: string,
|
||||
|
@ -72,23 +75,6 @@ export const getRecurrentVariableName = (name: string, existingNames: string[])
|
|||
return newName;
|
||||
};
|
||||
|
||||
export const getValuesFromQueryField = (queryString: string) => {
|
||||
const validQuery = `${queryString} ""`;
|
||||
const { root } = parse(validQuery);
|
||||
const lastCommand = root.commands[root.commands.length - 1];
|
||||
const columns: ESQLColumn[] = [];
|
||||
|
||||
walk(lastCommand, {
|
||||
visitColumn: (node) => columns.push(node),
|
||||
});
|
||||
|
||||
const column = Walker.match(lastCommand, { type: 'column' });
|
||||
|
||||
if (column) {
|
||||
return `${column.name}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const getFlyoutStyling = () => {
|
||||
return `
|
||||
.euiFlyoutBody__overflow {
|
||||
|
@ -103,19 +89,6 @@ export const getFlyoutStyling = () => {
|
|||
`;
|
||||
};
|
||||
|
||||
export const appendStatsByToQuery = (queryString: string, column: string) => {
|
||||
const { root } = parse(queryString);
|
||||
const lastCommand = root.commands[root.commands.length - 1];
|
||||
if (lastCommand.name === 'stats') {
|
||||
const statsCommand = lastCommand;
|
||||
mutate.generic.commands.remove(root, statsCommand);
|
||||
const queryWithoutStats = BasicPrettyPrinter.print(root);
|
||||
return `${queryWithoutStats}\n| STATS BY ${column}`;
|
||||
} else {
|
||||
return `${queryString}\n| STATS BY ${column}`;
|
||||
}
|
||||
};
|
||||
|
||||
export const validateVariableName = (variableName: string) => {
|
||||
let text = variableName
|
||||
// variable name can only contain letters, numbers and underscores
|
||||
|
|
|
@ -37,6 +37,7 @@ jest.mock('@kbn/esql-utils', () => {
|
|||
getIndexPatternFromESQLQuery: jest.fn().mockReturnValue('index1'),
|
||||
getLimitFromESQLQuery: jest.fn().mockReturnValue(1000),
|
||||
isQueryWrappedByPipes: jest.fn().mockReturnValue(false),
|
||||
getValuesFromQueryField: jest.fn().mockReturnValue('field'),
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -249,9 +250,6 @@ describe('ValueControlForm', () => {
|
|||
`Values from a query`
|
||||
);
|
||||
|
||||
// code editor should be rendered
|
||||
expect(await findByTestId('ESQLEditor')).toBeInTheDocument();
|
||||
|
||||
// values preview panel should be rendered
|
||||
expect(await findByTestId('esqlValuesPreview')).toBeInTheDocument();
|
||||
});
|
||||
|
|
|
@ -21,9 +21,14 @@ import {
|
|||
import { css } from '@emotion/react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { ISearchGeneric } from '@kbn/search-types';
|
||||
import ESQLEditor from '@kbn/esql-editor';
|
||||
import { ESQLVariableType, ESQLControlVariable } from '@kbn/esql-validation-autocomplete';
|
||||
import { getIndexPatternFromESQLQuery, getESQLResults } from '@kbn/esql-utils';
|
||||
import {
|
||||
getIndexPatternFromESQLQuery,
|
||||
getESQLResults,
|
||||
appendStatsByToQuery,
|
||||
getValuesFromQueryField,
|
||||
} from '@kbn/esql-utils';
|
||||
import { ESQLLangEditor } from '../../../create_editor';
|
||||
import type { ESQLControlState, ControlWidthOptions } from '../types';
|
||||
import {
|
||||
Header,
|
||||
|
@ -35,9 +40,7 @@ import {
|
|||
} from './shared_form_components';
|
||||
import {
|
||||
getRecurrentVariableName,
|
||||
getValuesFromQueryField,
|
||||
getFlyoutStyling,
|
||||
appendStatsByToQuery,
|
||||
areValuesIntervalsValid,
|
||||
validateVariableName,
|
||||
} from './helpers';
|
||||
|
@ -366,7 +369,7 @@ export function ValueControlForm({
|
|||
})}
|
||||
fullWidth
|
||||
>
|
||||
<ESQLEditor
|
||||
<ESQLLangEditor
|
||||
query={{ esql: valuesQuery }}
|
||||
onTextLangQueryChange={(q) => {
|
||||
setValuesQuery(q.esql);
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
"@kbn/kibana-utils-plugin",
|
||||
"@kbn/esql-validation-autocomplete",
|
||||
"@kbn/monaco",
|
||||
"@kbn/esql-ast",
|
||||
"@kbn/search-types",
|
||||
"@kbn/react-kibana-context-render",
|
||||
"@kbn/react-kibana-mount",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue