[ES|QL][Controls] Listen to ?_tstart and ?_tend named params (#225054)

## Summary

Passes the timeRange into the `getESQLResults` in order the queries witj
`_tstart` and `_tend` to work properly

<img width="1280" alt="image"
src="https://github.com/user-attachments/assets/4f03b0c7-6d3c-40e2-8775-b2d9b2f22c02"
/>


### Checklist
- [ ] [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
This commit is contained in:
Stratoula Kalafateli 2025-06-25 11:31:30 +02:00 committed by GitHub
parent f9d2c33852
commit a07c4608e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 75 additions and 8 deletions

View file

@ -102,7 +102,11 @@ export class EsqlPlugin implements Plugin<{}, EsqlPluginStart> {
const { CreateESQLControlAction } = await import(
'./triggers/esql_controls/esql_control_action'
);
const createESQLControlAction = new CreateESQLControlAction(core, data.search.search);
const createESQLControlAction = new CreateESQLControlAction(
core,
data.search.search,
data.query.timefilter.timefilter
);
return createESQLControlAction;
});

View file

@ -10,6 +10,7 @@
import React, { useCallback, useMemo, useState, useEffect } from 'react';
import { EuiFlyoutBody } from '@elastic/eui';
import { css } from '@emotion/react';
import type { TimeRange } from '@kbn/es-query';
import { ESQLVariableType, type ESQLControlVariable, type ESQLControlState } from '@kbn/esql-types';
import { getValuesFromQueryField } from '@kbn/esql-utils';
import { EsqlControlType, VariableNamePrefix } from '@kbn/esql-types';
@ -35,6 +36,7 @@ interface ESQLControlsFlyoutProps {
initialVariableType: ESQLVariableType;
queryString: string;
esqlVariables: ESQLControlVariable[];
timeRange?: TimeRange;
onSaveControl?: (controlState: ESQLControlState, updatedQuery: string) => Promise<void>;
onCancelControl?: () => void;
cursorPosition?: monaco.Position;
@ -47,6 +49,7 @@ export function ESQLControlsFlyout({
initialVariableType,
queryString,
esqlVariables,
timeRange,
onSaveControl,
onCancelControl,
cursorPosition,
@ -185,6 +188,7 @@ export function ESQLControlsFlyout({
setControlState={setControlState}
search={search}
valuesRetrieval={valuesField}
timeRange={timeRange}
/>
) : (
<IdentifierControlForm

View file

@ -8,12 +8,13 @@
*/
import React from 'react';
import { render, within, fireEvent } from '@testing-library/react';
import { render, within, fireEvent, waitFor } from '@testing-library/react';
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
import { IUiSettingsClient } from '@kbn/core/public';
import { monaco } from '@kbn/monaco';
import { coreMock } from '@kbn/core/server/mocks';
import { ESQLVariableType, EsqlControlType, ESQLControlState } from '@kbn/esql-types';
import { getESQLResults } from '@kbn/esql-utils';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { ESQLControlsFlyout } from '.';
@ -294,6 +295,33 @@ describe('ValueControlForm', () => {
const identifiersOptionsDropdown = await findByTestId('esqlIdentifiersOptions');
expect(identifiersOptionsDropdown).toBeInTheDocument();
});
it('should call getESQLResults with the provided timeRange when query is submitted', async () => {
const mockTimeRange = { from: '2023-01-01', to: '2023-01-02' };
render(
<IntlProvider locale="en">
<ESQLControlsFlyout
initialVariableType={ESQLVariableType.VALUES}
queryString="FROM foo | WHERE field =="
onSaveControl={jest.fn()}
closeFlyout={jest.fn()}
onCancelControl={jest.fn()}
search={searchMock}
esqlVariables={[]}
timeRange={mockTimeRange}
/>
</IntlProvider>
);
await waitFor(() => {
expect(getESQLResults).toHaveBeenCalledWith(
expect.objectContaining({
timeRange: mockTimeRange,
})
);
});
});
});
});
});

View file

@ -20,6 +20,7 @@ import {
EuiPanel,
} from '@elastic/eui';
import { css } from '@emotion/react';
import type { TimeRange } from '@kbn/es-query';
import { FormattedMessage } from '@kbn/i18n-react';
import type { ISearchGeneric } from '@kbn/search-types';
import {
@ -46,12 +47,15 @@ interface ValueControlFormProps {
setControlState: (state: ESQLControlState) => void;
initialState?: ESQLControlState;
valuesRetrieval?: string;
timeRange?: TimeRange;
}
const SUGGESTED_INTERVAL_VALUES = ['5 minutes', '1 hour', '1 day', '1 week', '1 month'];
const INITIAL_EMPTY_STATE_QUERY = `/** Example
To get the agent field values use:
FROM logs-* | STATS BY agent
FROM logs-*
| WHERE @timestamp <=?_tend and @timestamp >?_tstart
| STATS BY agent
*/`;
export function ValueControlForm({
@ -63,6 +67,7 @@ export function ValueControlForm({
search,
setControlState,
valuesRetrieval,
timeRange,
}: ValueControlFormProps) {
const isMounted = useMountedState();
@ -157,6 +162,7 @@ export function ValueControlForm({
signal: undefined,
filter: undefined,
dropNullColumns: true,
timeRange,
}).then((results) => {
if (!isMounted()) {
return;
@ -185,7 +191,7 @@ export function ValueControlForm({
setEsqlQueryErrors([e]);
}
},
[isMounted, search]
[isMounted, search, timeRange]
);
useEffect(() => {

View file

@ -16,6 +16,7 @@ import { CreateESQLControlAction } from './esql_control_action';
describe('update ES|QL query action', () => {
const dataMock = dataPluginMock.createStartContract();
const searchMock = dataMock.search.search;
const timefilterMock = dataMock.query.timefilter.timefilter;
const core = coreMock.createStart();
const coreStart = {
...core,
@ -28,7 +29,11 @@ describe('update ES|QL query action', () => {
} as CoreStart;
describe('compatibility check', () => {
it('is compatible if queryString is given', async () => {
const createControlAction = new CreateESQLControlAction(coreStart, searchMock);
const createControlAction = new CreateESQLControlAction(
coreStart,
searchMock,
timefilterMock
);
const isCompatible = await createControlAction.isCompatible({
queryString: 'FROM index',
variableType: ESQLVariableType.FIELDS,
@ -48,7 +53,11 @@ describe('update ES|QL query action', () => {
},
},
} as CoreStart;
const createControlAction = new CreateESQLControlAction(coreStartESQLDidabled, searchMock);
const createControlAction = new CreateESQLControlAction(
coreStartESQLDidabled,
searchMock,
timefilterMock
);
const isCompatible = await createControlAction.isCompatible({
queryString: '',
variableType: ESQLVariableType.FIELDS,
@ -59,7 +68,11 @@ describe('update ES|QL query action', () => {
});
it('is incompatible if variableType is invalid', async () => {
const createControlAction = new CreateESQLControlAction(coreStart, searchMock);
const createControlAction = new CreateESQLControlAction(
coreStart,
searchMock,
timefilterMock
);
const isCompatible = await createControlAction.isCompatible({
queryString: 'FROM index',
variableType: 'INVALID_TYPE' as ESQLVariableType,

View file

@ -10,6 +10,7 @@
import { i18n } from '@kbn/i18n';
import type { Action } from '@kbn/ui-actions-plugin/public';
import type { CoreStart } from '@kbn/core/public';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import type { ISearchGeneric } from '@kbn/search-types';
import type { ESQLVariableType, ESQLControlState } from '@kbn/esql-types';
import type { ESQLControlVariable } from '@kbn/esql-types';
@ -32,7 +33,11 @@ export class CreateESQLControlAction implements Action<Context> {
public id = ACTION_CREATE_ESQL_CONTROL;
public order = 50;
constructor(protected readonly core: CoreStart, protected readonly search: ISearchGeneric) {}
constructor(
protected readonly core: CoreStart,
protected readonly search: ISearchGeneric,
protected readonly timefilter: TimefilterContract
) {}
public getDisplayName(): string {
return i18n.translate('esql.createESQLControlLabel', {
@ -61,6 +66,7 @@ export class CreateESQLControlAction implements Action<Context> {
queryString,
core: this.core,
search: this.search,
timefilter: this.timefilter,
variableType,
esqlVariables,
onSaveControl,

View file

@ -9,6 +9,7 @@
import React, { lazy, Suspense, Fragment } from 'react';
import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import type { CoreStart } from '@kbn/core/public';
import type { ISearchGeneric } from '@kbn/search-types';
@ -22,6 +23,7 @@ interface Context {
queryString: string;
core: CoreStart;
search: ISearchGeneric;
timefilter: TimefilterContract;
variableType: ESQLVariableType;
esqlVariables: ESQLControlVariable[];
onSaveControl?: (controlState: ESQLControlState, updatedQuery: string) => Promise<void>;
@ -44,6 +46,7 @@ export async function executeAction({
queryString,
core,
search,
timefilter,
variableType,
esqlVariables,
onSaveControl,
@ -62,6 +65,8 @@ export async function executeAction({
};
});
const timeRange = timefilter.getTime();
const deps = await untilPluginStartServicesReady();
const handle = core.overlays.openFlyout(
toMountPoint(
@ -85,6 +90,7 @@ export async function executeAction({
cursorPosition={cursorPosition}
initialState={initialState}
esqlVariables={esqlVariables}
timeRange={timeRange}
/>
</Suspense>
</KibanaContextProvider>