diff --git a/public/app/plugins/datasource/loki/LanguageProvider.test.ts b/public/app/plugins/datasource/loki/LanguageProvider.test.ts index 51b91113b18..1cd28b22b9b 100644 --- a/public/app/plugins/datasource/loki/LanguageProvider.test.ts +++ b/public/app/plugins/datasource/loki/LanguageProvider.test.ts @@ -407,7 +407,11 @@ describe('Query imports', () => { maxLines: DEFAULT_MAX_LINES_SAMPLE, refId: 'data-samples', }, - undefined + // mocked default time range + expect.objectContaining({ + from: 0, + to: 1, + }) ); }); @@ -428,7 +432,11 @@ describe('Query imports', () => { maxLines: 5, refId: 'data-samples', }, - undefined + // mocked default time range + expect.objectContaining({ + from: 0, + to: 1, + }) ); }); diff --git a/public/app/plugins/datasource/loki/LanguageProvider.ts b/public/app/plugins/datasource/loki/LanguageProvider.ts index 962e9b5436a..8452ac1f3ce 100644 --- a/public/app/plugins/datasource/loki/LanguageProvider.ts +++ b/public/app/plugins/datasource/loki/LanguageProvider.ts @@ -267,7 +267,7 @@ export default class LokiLanguageProvider extends LanguageProvider { refId: 'data-samples', maxLines: options?.maxLines || DEFAULT_MAX_LINES_SAMPLE, }, - options?.timeRange + options?.timeRange ?? this.getDefaultTimeRange() ); if (!series.length) { diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 47a2dbf8606..743b1be7e31 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -1474,26 +1474,26 @@ describe('LokiDatasource', () => { }); it('ignores invalid queries', () => { const spy = jest.spyOn(ds, 'query'); - ds.getDataSamples({ expr: 'not a query', refId: 'A' }); + ds.getDataSamples({ expr: 'not a query', refId: 'A' }, mockTimeRange); expect(spy).not.toHaveBeenCalled(); }); it('ignores metric queries', () => { const spy = jest.spyOn(ds, 'query'); - ds.getDataSamples({ expr: 'count_over_time({a="b"}[1m])', refId: 'A' }); + ds.getDataSamples({ expr: 'count_over_time({a="b"}[1m])', refId: 'A' }, mockTimeRange); expect(spy).not.toHaveBeenCalled(); }); it('uses the current interval in the request', () => { const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse)); - ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }); + ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }, mockTimeRange); expect(spy).toHaveBeenCalledWith( expect.objectContaining({ - range: ds.getTimeRange(), + range: mockTimeRange, }) ); }); it('hides the request from the inspector', () => { const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse)); - ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }); + ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }, mockTimeRange); expect(spy).toHaveBeenCalledWith( expect.objectContaining({ hideFromInspector: true, @@ -1503,7 +1503,7 @@ describe('LokiDatasource', () => { }); it('sets the supporting query type in the request', () => { const spy = jest.spyOn(ds, 'query').mockImplementation(() => of({} as DataQueryResponse)); - ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }); + ds.getDataSamples({ expr: '{job="bar"}', refId: 'A' }, mockTimeRange); expect(spy).toHaveBeenCalledWith( expect.objectContaining({ targets: [expect.objectContaining({ supportingQueryType: SupportingQueryType.DataSample })], @@ -1828,18 +1828,18 @@ describe('makeStatsRequest', () => { }); }); -describe('getTimeRange*()', () => { - it('exposes the current time range', () => { +describe('getTimeRangeParams()', () => { + it('turns time range into a Loki range parameters', () => { const ds = createLokiDatasource(); - const timeRange = ds.getTimeRange(); - - expect(timeRange.from).toBeDefined(); - expect(timeRange.to).toBeDefined(); - }); - - it('exposes time range as params', () => { - const ds = createLokiDatasource(); - const params = ds.getTimeRangeParams(); + const range = { + from: dateTime(1524650400000), + to: dateTime(1524654000000), + raw: { + from: 'now-1h', + to: 'now', + }, + }; + const params = ds.getTimeRangeParams(range); // Returns a very big integer, so we stringify it for the assertion expect(JSON.stringify(params)).toEqual('{"start":1524650400000000000,"end":1524654000000000000}'); diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index 664f0014a1f..d76636f6b9f 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -45,7 +45,6 @@ import { import { Duration } from '@grafana/lezer-logql'; import { BackendSrvRequest, config, DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; -import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { queryLogsSample, queryLogsVolume } from '../../../features/logs/logsModel'; import { getLogLevelFromKey } from '../../../features/logs/utils'; @@ -152,8 +151,7 @@ export class LokiDatasource constructor( private instanceSettings: DataSourceInstanceSettings, - private readonly templateSrv: TemplateSrv = getTemplateSrv(), - private readonly timeSrv: TimeSrv = getTimeSrv() + private readonly templateSrv: TemplateSrv = getTemplateSrv() ) { super(instanceSettings); @@ -455,21 +453,12 @@ export class LokiDatasource return query.expr; } - /** - * Retrieve the current time range. - * @returns The current time range as provided by the timeSrv. - */ - getTimeRange() { - return this.timeSrv.timeRange(); - } - /** * Given a time range, returns it as Loki parameters. * @returns An object containing the start and end times in nanoseconds since the Unix epoch. */ - getTimeRangeParams(timeRange?: TimeRange) { - const range = timeRange ?? this.getTimeRange(); - return { start: range.from.valueOf() * NS_IN_MS, end: range.to.valueOf() * NS_IN_MS }; + getTimeRangeParams(timeRange: TimeRange) { + return { start: timeRange.from.valueOf() * NS_IN_MS, end: timeRange.to.valueOf() * NS_IN_MS }; } /** @@ -734,7 +723,7 @@ export class LokiDatasource * Currently, it works for logs data only. * @returns A Promise that resolves to an array of DataFrames containing data samples. */ - async getDataSamples(query: LokiQuery, timeRange?: TimeRange): Promise { + async getDataSamples(query: LokiQuery, timeRange: TimeRange): Promise { // Currently works only for logs sample if (!isLogsQuery(query.expr) || isQueryWithError(this.interpolateString(query.expr, placeHolderScopedVars))) { return []; @@ -748,8 +737,7 @@ export class LokiDatasource supportingQueryType: SupportingQueryType.DataSample, }; - const range = timeRange ?? this.getTimeRange(); - const request = makeRequest(lokiLogsQuery, range, CoreApp.Unknown, REF_ID_DATA_SAMPLES, true); + const request = makeRequest(lokiLogsQuery, timeRange, CoreApp.Unknown, REF_ID_DATA_SAMPLES, true); return await lastValueFrom(this.query(request).pipe(switchMap((res) => of(res.data)))); } diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx index 0f1b76e1c28..6e6612541d0 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilder.tsx @@ -99,13 +99,14 @@ export const LokiQueryBuilder = React.memo( useEffect(() => { const onGetSampleData = async () => { const lokiQuery = { expr: lokiQueryModeller.renderQuery(query), refId: 'data-samples' }; - const series = await datasource.getDataSamples(lokiQuery); - const sampleData = { series, state: LoadingState.Done, timeRange: getDefaultTimeRange() }; + const range = timeRange ?? getDefaultTimeRange(); + const series = await datasource.getDataSamples(lokiQuery, range); + const sampleData = { series, state: LoadingState.Done, timeRange: range }; setSampleData(sampleData); }; onGetSampleData().catch(console.error); - }, [datasource, query]); + }, [datasource, query, timeRange]); const lang = { grammar: logqlGrammar, name: 'logql' }; return ( diff --git a/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx b/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx index 4e364fc8290..399d72dfd08 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/UnwrapParamEditor.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { SelectableValue, toOption } from '@grafana/data'; +import { SelectableValue, getDefaultTimeRange, toOption } from '@grafana/data'; import { Select } from '@grafana/ui'; import { getOperationParamId } from '../../../prometheus/querybuilder/shared/operationUtils'; @@ -19,6 +19,7 @@ export function UnwrapParamEditor({ value, query, datasource, + timeRange, }: QueryBuilderOperationParamEditorProps) { const [state, setState] = useState<{ options?: Array>; @@ -32,7 +33,7 @@ export function UnwrapParamEditor({ // This check is always true, we do it to make typescript happy if (datasource instanceof LokiDatasource) { setState({ isLoading: true }); - const options = await loadUnwrapOptions(query, datasource); + const options = await loadUnwrapOptions(query, datasource, timeRange); setState({ options, isLoading: undefined }); } }} @@ -53,7 +54,8 @@ export function UnwrapParamEditor({ async function loadUnwrapOptions( query: LokiVisualQuery, - datasource: LokiDatasource + datasource: LokiDatasource, + timeRange = getDefaultTimeRange() ): Promise>> { const queryExpr = lokiQueryModeller.renderQuery(query); const logExpr = getLogQueryFromMetricsQuery(queryExpr); @@ -61,7 +63,7 @@ async function loadUnwrapOptions( return []; } - const samples = await datasource.getDataSamples({ expr: logExpr, refId: 'unwrap_samples' }); + const samples = await datasource.getDataSamples({ expr: logExpr, refId: 'unwrap_samples' }, timeRange); const unwrapLabels = extractUnwrapLabelKeysFromDataFrame(samples[0]); const labelOptions = unwrapLabels.map((label) => ({ diff --git a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationEditor.tsx b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationEditor.tsx index 4ca3b2855cd..bbb40f9fa47 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationEditor.tsx +++ b/public/app/plugins/datasource/prometheus/querybuilder/shared/OperationEditor.tsx @@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css'; import React, { useEffect, useId, useState } from 'react'; import { Draggable } from 'react-beautiful-dnd'; -import { DataSourceApi, GrafanaTheme2 } from '@grafana/data'; +import { DataSourceApi, GrafanaTheme2, TimeRange } from '@grafana/data'; import { Button, Icon, InlineField, Tooltip, useTheme2, Stack } from '@grafana/ui'; import { isConflictingFilter } from 'app/plugins/datasource/loki/querybuilder/operationUtils'; import { LokiOperationId } from 'app/plugins/datasource/loki/querybuilder/types'; @@ -29,6 +29,7 @@ export interface Props { onRunQuery: () => void; flash?: boolean; highlight?: boolean; + timeRange?: TimeRange; } export function OperationEditor({ @@ -42,6 +43,7 @@ export function OperationEditor({ datasource, flash, highlight, + timeRange, }: Props) { const def = queryModeller.getOperationDef(operation.id); const shouldFlash = useFlash(flash); @@ -106,6 +108,7 @@ export function OperationEditor({ onRunQuery={onRunQuery} query={query} datasource={datasource} + timeRange={timeRange} /> {paramDef.restParam && (operation.params.length > def.params.length || paramDef.optional) && (