Tempo: Tidy up and organise (#90649)

* Move test files to folder

* Update paths

* Tidy up
This commit is contained in:
Joey 2024-08-12 14:12:22 +01:00 committed by GitHub
parent 6dce2ecbde
commit 379249fc60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 158 additions and 160 deletions

View File

@ -5,7 +5,7 @@ import { useState } from 'react';
import { TraceqlSearchScope } from '../dataquery.gen'; import { TraceqlSearchScope } from '../dataquery.gen';
import { TempoDatasource } from '../datasource'; import { TempoDatasource } from '../datasource';
import TempoLanguageProvider from '../language_provider'; import TempoLanguageProvider from '../language_provider';
import { initTemplateSrv } from '../test_utils'; import { initTemplateSrv } from '../test/test_utils';
import { TempoQuery } from '../types'; import { TempoQuery } from '../types';
import { GroupByField } from './GroupByField'; import { GroupByField } from './GroupByField';

View File

@ -6,7 +6,7 @@ import { LanguageProvider } from '@grafana/data';
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
import { TempoDatasource } from '../datasource'; import { TempoDatasource } from '../datasource';
import TempoLanguageProvider from '../language_provider'; import TempoLanguageProvider from '../language_provider';
import { initTemplateSrv } from '../test_utils'; import { initTemplateSrv } from '../test/test_utils';
import { keywordOperators, numberOperators, operators, stringOperators } from '../traceql/traceql'; import { keywordOperators, numberOperators, operators, stringOperators } from '../traceql/traceql';
import SearchField from './SearchField'; import SearchField from './SearchField';

View File

@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
import { TempoDatasource } from '../datasource'; import { TempoDatasource } from '../datasource';
import TempoLanguageProvider from '../language_provider'; import TempoLanguageProvider from '../language_provider';
import { initTemplateSrv } from '../test_utils'; import { initTemplateSrv } from '../test/test_utils';
import { Scope } from '../types'; import { Scope } from '../types';
import TagsInput from './TagsInput'; import TagsInput from './TagsInput';

View File

@ -7,7 +7,7 @@ import { config } from '@grafana/runtime';
import { TraceqlSearchScope } from '../dataquery.gen'; import { TraceqlSearchScope } from '../dataquery.gen';
import { TempoDatasource } from '../datasource'; import { TempoDatasource } from '../datasource';
import TempoLanguageProvider from '../language_provider'; import TempoLanguageProvider from '../language_provider';
import { initTemplateSrv } from '../test_utils'; import { initTemplateSrv } from '../test/test_utils';
import { TempoQuery } from '../types'; import { TempoQuery } from '../types';
import TraceQLSearch from './TraceQLSearch'; import TraceQLSearch from './TraceQLSearch';

View File

@ -10,7 +10,7 @@ import {
TempoVariableQueryType, TempoVariableQueryType,
} from './VariableQueryEditor'; } from './VariableQueryEditor';
import { selectOptionInTest } from './_importedDependencies/test/helpers/selectOptionInTest'; import { selectOptionInTest } from './_importedDependencies/test/helpers/selectOptionInTest';
import { createTempoDatasource } from './mocks'; import { createTempoDatasource } from './test/mocks';
const refId = 'TempoDatasourceVariableQueryEditor-VariableQuery'; const refId = 'TempoDatasourceVariableQueryEditor-VariableQuery';

View File

@ -41,10 +41,10 @@ import {
getFieldConfig, getFieldConfig,
getEscapedSpanNames, getEscapedSpanNames,
} from './datasource'; } from './datasource';
import mockJson from './mockJsonResponse.json'; import mockJson from './test/mockJsonResponse.json';
import mockServiceGraph from './mockServiceGraph.json'; import mockServiceGraph from './test/mockServiceGraph.json';
import { createMetadataRequest, createTempoDatasource } from './mocks'; import { createMetadataRequest, createTempoDatasource } from './test/mocks';
import { initTemplateSrv } from './test_utils'; import { initTemplateSrv } from './test/test_utils';
import { TempoJsonData, TempoQuery } from './types'; import { TempoJsonData, TempoQuery } from './types';
let mockObservable: () => Observable<unknown>; let mockObservable: () => Observable<unknown>;
@ -68,7 +68,7 @@ describe('Tempo data source', () => {
describe('runs correctly', () => { describe('runs correctly', () => {
config.featureToggles.traceQLStreaming = true; config.featureToggles.traceQLStreaming = true;
jest.spyOn(TempoDatasource.prototype, 'isFeatureAvailable').mockImplementation(() => true); jest.spyOn(TempoDatasource.prototype, 'isFeatureAvailable').mockImplementation(() => true);
const handleStreamingSearch = jest.spyOn(TempoDatasource.prototype, 'handleStreamingSearch'); const handleStreamingQuery = jest.spyOn(TempoDatasource.prototype, 'handleStreamingQuery');
const request = jest.spyOn(TempoDatasource.prototype, '_request'); const request = jest.spyOn(TempoDatasource.prototype, '_request');
const templateSrv: TemplateSrv = { replace: (s: string) => s } as unknown as TemplateSrv; const templateSrv: TemplateSrv = { replace: (s: string) => s } as unknown as TemplateSrv;
@ -104,7 +104,7 @@ describe('Tempo data source', () => {
config.liveEnabled = true; config.liveEnabled = true;
const ds = new TempoDatasource(defaultSettings, templateSrv); const ds = new TempoDatasource(defaultSettings, templateSrv);
await lastValueFrom(ds.query(traceqlQuery as DataQueryRequest<TempoQuery>)); await lastValueFrom(ds.query(traceqlQuery as DataQueryRequest<TempoQuery>));
expect(handleStreamingSearch).toHaveBeenCalledTimes(1); expect(handleStreamingQuery).toHaveBeenCalledTimes(1);
expect(request).toHaveBeenCalledTimes(0); expect(request).toHaveBeenCalledTimes(0);
}); });
@ -112,7 +112,7 @@ describe('Tempo data source', () => {
config.liveEnabled = true; config.liveEnabled = true;
const ds = new TempoDatasource(defaultSettings, templateSrv); const ds = new TempoDatasource(defaultSettings, templateSrv);
await lastValueFrom(ds.query(traceqlSearchQuery as DataQueryRequest<TempoQuery>)); await lastValueFrom(ds.query(traceqlSearchQuery as DataQueryRequest<TempoQuery>));
expect(handleStreamingSearch).toHaveBeenCalledTimes(1); expect(handleStreamingQuery).toHaveBeenCalledTimes(1);
expect(request).toHaveBeenCalledTimes(0); expect(request).toHaveBeenCalledTimes(0);
}); });
@ -120,7 +120,7 @@ describe('Tempo data source', () => {
config.liveEnabled = false; config.liveEnabled = false;
const ds = new TempoDatasource(defaultSettings, templateSrv); const ds = new TempoDatasource(defaultSettings, templateSrv);
await lastValueFrom(ds.query(traceqlQuery as DataQueryRequest<TempoQuery>)); await lastValueFrom(ds.query(traceqlQuery as DataQueryRequest<TempoQuery>));
expect(handleStreamingSearch).toHaveBeenCalledTimes(1); expect(handleStreamingQuery).toHaveBeenCalledTimes(1);
expect(request).toHaveBeenCalledTimes(1); expect(request).toHaveBeenCalledTimes(1);
}); });
@ -128,7 +128,7 @@ describe('Tempo data source', () => {
config.liveEnabled = false; config.liveEnabled = false;
const ds = new TempoDatasource(defaultSettings, templateSrv); const ds = new TempoDatasource(defaultSettings, templateSrv);
await lastValueFrom(ds.query(traceqlSearchQuery as DataQueryRequest<TempoQuery>)); await lastValueFrom(ds.query(traceqlSearchQuery as DataQueryRequest<TempoQuery>));
expect(handleStreamingSearch).toHaveBeenCalledTimes(1); expect(handleStreamingQuery).toHaveBeenCalledTimes(1);
expect(request).toHaveBeenCalledTimes(1); expect(request).toHaveBeenCalledTimes(1);
}); });
}); });
@ -365,14 +365,14 @@ describe('Tempo data source', () => {
describe('test the testDatasource function', () => { describe('test the testDatasource function', () => {
it('should return a success msg if response.ok is true', async () => { it('should return a success msg if response.ok is true', async () => {
mockObservable = () => of({ ok: true }); mockObservable = () => of({ ok: true });
const handleStreamingSearch = jest const handleStreamingQuery = jest
.spyOn(TempoDatasource.prototype, 'handleStreamingSearch') .spyOn(TempoDatasource.prototype, 'handleStreamingQuery')
.mockImplementation(() => of({ data: [] })); .mockImplementation(() => of({ data: [] }));
const ds = new TempoDatasource(defaultSettings); const ds = new TempoDatasource(defaultSettings);
const response = await ds.testDatasource(); const response = await ds.testDatasource();
expect(response.status).toBe('success'); expect(response.status).toBe('success');
expect(handleStreamingSearch).toHaveBeenCalled(); expect(handleStreamingQuery).toHaveBeenCalled();
}); });
}); });
@ -397,7 +397,7 @@ describe('Tempo data source', () => {
raw: { from: 'now-15m', to: 'now' }, raw: { from: 'now-15m', to: 'now' },
}; };
const request = ds.traceIdQueryRequest( const request = ds.makeTraceIdRequest(
{ {
requestId: 'test', requestId: 'test',
interval: '', interval: '',
@ -426,7 +426,7 @@ describe('Tempo data source', () => {
jsonData: { traceQuery: { timeShiftEnabled: false, spanStartTimeShift: '2m', spanEndTimeShift: '4m' } }, jsonData: { traceQuery: { timeShiftEnabled: false, spanStartTimeShift: '2m', spanEndTimeShift: '4m' } },
}); });
const request = ds.traceIdQueryRequest( const request = ds.makeTraceIdRequest(
{ {
requestId: 'test', requestId: 'test',
interval: '', interval: '',

View File

@ -283,6 +283,19 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
); );
} }
isTraceQlMetricsQuery(query: string): boolean {
// Check whether this is a metrics query by checking if it contains a metrics function
const metricsFnRegex =
/\|\s*(rate|count_over_time|avg_over_time|max_over_time|min_over_time|quantile_over_time|histogram_over_time|compare)\s*\(/;
return !!query.trim().match(metricsFnRegex);
}
isTraceIdQuery(query: string): boolean {
const hexOnlyRegex = /^[0-9A-Fa-f]*$/;
// Check whether this is a trace ID or traceQL query by checking if it only contains hex characters
return !!query.trim().match(hexOnlyRegex);
}
query(options: DataQueryRequest<TempoQuery>): Observable<DataQueryResponse> { query(options: DataQueryRequest<TempoQuery>): Observable<DataQueryResponse> {
const subQueries: Array<Observable<DataQueryResponse>> = []; const subQueries: Array<Observable<DataQueryResponse>> = [];
const filteredTargets = options.targets.filter((target) => !target.hide); const filteredTargets = options.targets.filter((target) => !target.hide);
@ -358,7 +371,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
if (target) { if (target) {
const appliedQuery = this.applyVariables(target, options.scopedVars); const appliedQuery = this.applyVariables(target, options.scopedVars);
subQueries.push( subQueries.push(
this.handleMetricsSummary(appliedQuery, generateQueryFromFilters(appliedQuery.filters), options) this.handleMetricsSummaryQuery(appliedQuery, generateQueryFromFilters(appliedQuery.filters), options)
); );
} }
} }
@ -379,7 +392,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
}); });
if (this.isStreamingSearchEnabled()) { if (this.isStreamingSearchEnabled()) {
subQueries.push(this.handleStreamingSearch(options, traceqlSearchTargets, queryValueFromFilters)); subQueries.push(this.handleStreamingQuery(options, traceqlSearchTargets, queryValueFromFilters));
} else { } else {
subQueries.push( subQueries.push(
this._request('/api/search', { this._request('/api/search', {
@ -459,19 +472,6 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
return merge(...subQueries); return merge(...subQueries);
} }
isTraceQlMetricsQuery(query: string): boolean {
// Check whether this is a metrics query by checking if it contains a metrics function
const metricsFnRegex =
/\|\s*(rate|count_over_time|avg_over_time|max_over_time|min_over_time|quantile_over_time|histogram_over_time|compare)\s*\(/;
return !!query.trim().match(metricsFnRegex);
}
isTraceIdQuery(query: string): boolean {
const hexOnlyRegex = /^[0-9A-Fa-f]*$/;
// Check whether this is a trace ID or traceQL query by checking if it only contains hex characters
return !!query.trim().match(hexOnlyRegex);
}
applyTemplateVariables(query: TempoQuery, scopedVars: ScopedVars) { applyTemplateVariables(query: TempoQuery, scopedVars: ScopedVars) {
return this.applyVariables(query, scopedVars); return this.applyVariables(query, scopedVars);
} }
@ -517,7 +517,104 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
}; };
} }
handleMetricsSummary = (target: TempoQuery, query: string, options: DataQueryRequest<TempoQuery>) => { formatGroupBy = (groupBy: TraceqlFilter[]) => {
return groupBy
?.filter((f) => f.tag)
.map((f) => {
if (f.scope === TraceqlSearchScope.Unscoped) {
return `.${f.tag}`;
}
return f.scope !== TraceqlSearchScope.Intrinsic ? `${f.scope}.${f.tag}` : f.tag;
})
.join(', ');
};
hasGroupBy = (query: TempoQuery) => {
return query.groupBy?.find((gb) => gb.tag);
};
/**
* Handles the simplest of the queries where we have just a trace id and return trace data for it.
* @param options
* @param targets
* @private
*/
handleTraceIdQuery(options: DataQueryRequest<TempoQuery>, targets: TempoQuery[]): Observable<DataQueryResponse> {
const validTargets = targets
.filter((t) => t.query)
.map((t): TempoQuery => ({ ...t, query: t.query?.trim(), queryType: 'traceId' }));
if (!validTargets.length) {
return EMPTY;
}
const request = this.makeTraceIdRequest(options, validTargets);
return super.query(request).pipe(
map((response) => {
if (response.error) {
return response;
}
return transformTrace(response, this.instanceSettings, this.nodeGraph?.enabled);
})
);
}
handleTraceQlQuery = (
options: DataQueryRequest<TempoQuery>,
targets: {
[type: string]: TempoQuery[];
},
queryValue: string
): Observable<DataQueryResponse> => {
if (this.isStreamingSearchEnabled()) {
return this.handleStreamingQuery(options, targets.traceql, queryValue);
} else {
return this._request('/api/search', {
q: queryValue,
limit: options.targets[0].limit ?? DEFAULT_LIMIT,
spss: options.targets[0].spss ?? DEFAULT_SPSS,
start: options.range.from.unix(),
end: options.range.to.unix(),
}).pipe(
map((response) => {
return {
data: formatTraceQLResponse(response.data.traces, this.instanceSettings, targets.traceql[0].tableType),
};
}),
catchError((err) => {
return of({ error: { message: getErrorMessage(err.data.message) }, data: [] });
})
);
}
};
handleTraceQlMetricsQuery = (
options: DataQueryRequest<TempoQuery>,
queryValue: string
): Observable<DataQueryResponse> => {
const requestData = {
query: queryValue,
start: options.range.from.unix(),
end: options.range.to.unix(),
step: options.targets[0].step,
};
if (!requestData.step) {
delete requestData.step;
}
return this._request('/api/metrics/query_range', requestData).pipe(
map((response) => {
return {
data: formatTraceQLMetrics(queryValue, response.data),
};
}),
catchError((err) => {
return of({ error: { message: getErrorMessage(err.data.message) }, data: [] });
})
);
};
handleMetricsSummaryQuery = (target: TempoQuery, query: string, options: DataQueryRequest<TempoQuery>) => {
reportInteraction('grafana_traces_metrics_summary_queried', { reportInteraction('grafana_traces_metrics_summary_queried', {
datasourceType: 'tempo', datasourceType: 'tempo',
app: options.app ?? '', app: options.app ?? '',
@ -574,105 +671,30 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
); );
}; };
formatGroupBy = (groupBy: TraceqlFilter[]) => { // This function can probably be simplified by avoiding passing both `targets` and `query`,
return groupBy // since `query` is built from `targets`, if you look at how this function is currently called
?.filter((f) => f.tag) handleStreamingQuery(
.map((f) => { options: DataQueryRequest<TempoQuery>,
if (f.scope === TraceqlSearchScope.Unscoped) { targets: TempoQuery[],
return `.${f.tag}`; query: string
} ): Observable<DataQueryResponse> {
return f.scope !== TraceqlSearchScope.Intrinsic ? `${f.scope}.${f.tag}` : f.tag; if (query === '') {
})
.join(', ');
};
hasGroupBy = (query: TempoQuery) => {
return query.groupBy?.find((gb) => gb.tag);
};
/**
* Handles the simplest of the queries where we have just a trace id and return trace data for it.
* @param options
* @param targets
* @private
*/
handleTraceIdQuery(options: DataQueryRequest<TempoQuery>, targets: TempoQuery[]): Observable<DataQueryResponse> {
const validTargets = targets
.filter((t) => t.query)
.map((t): TempoQuery => ({ ...t, query: t.query?.trim(), queryType: 'traceId' }));
if (!validTargets.length) {
return EMPTY; return EMPTY;
} }
const traceRequest = this.traceIdQueryRequest(options, validTargets); return merge(
...targets.map((target) =>
return super.query(traceRequest).pipe( doTempoChannelStream(
map((response) => { { ...target, query },
if (response.error) { this, // the datasource
return response; options,
} this.instanceSettings
return transformTrace(response, this.instanceSettings, this.nodeGraph?.enabled); )
}) )
); );
} }
handleTraceQlQuery = ( makeTraceIdRequest(options: DataQueryRequest<TempoQuery>, targets: TempoQuery[]): DataQueryRequest<TempoQuery> {
options: DataQueryRequest<TempoQuery>,
targets: {
[type: string]: TempoQuery[];
},
queryValue: string
): Observable<DataQueryResponse> => {
if (this.isStreamingSearchEnabled()) {
return this.handleStreamingSearch(options, targets.traceql, queryValue);
} else {
return this._request('/api/search', {
q: queryValue,
limit: options.targets[0].limit ?? DEFAULT_LIMIT,
spss: options.targets[0].spss ?? DEFAULT_SPSS,
start: options.range.from.unix(),
end: options.range.to.unix(),
}).pipe(
map((response) => {
return {
data: formatTraceQLResponse(response.data.traces, this.instanceSettings, targets.traceql[0].tableType),
};
}),
catchError((err) => {
return of({ error: { message: getErrorMessage(err.data.message) }, data: [] });
})
);
}
};
handleTraceQlMetricsQuery = (
options: DataQueryRequest<TempoQuery>,
queryValue: string
): Observable<DataQueryResponse> => {
const requestData = {
query: queryValue,
start: options.range.from.unix(),
end: options.range.to.unix(),
step: options.targets[0].step,
};
if (!requestData.step) {
delete requestData.step;
}
return this._request('/api/metrics/query_range', requestData).pipe(
map((response) => {
return {
data: formatTraceQLMetrics(queryValue, response.data),
};
}),
catchError((err) => {
return of({ error: { message: getErrorMessage(err.data.message) }, data: [] });
})
);
};
traceIdQueryRequest(options: DataQueryRequest<TempoQuery>, targets: TempoQuery[]): DataQueryRequest<TempoQuery> {
const request = { const request = {
...options, ...options,
targets, targets,
@ -697,29 +719,6 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
return request; return request;
} }
// This function can probably be simplified by avoiding passing both `targets` and `query`,
// since `query` is built from `targets`, if you look at how this function is currently called
handleStreamingSearch(
options: DataQueryRequest<TempoQuery>,
targets: TempoQuery[],
query: string
): Observable<DataQueryResponse> {
if (query === '') {
return EMPTY;
}
return merge(
...targets.map((target) =>
doTempoChannelStream(
{ ...target, query },
this, // the datasource
options,
this.instanceSettings
)
)
);
}
async metadataRequest(url: string, params = {}) { async metadataRequest(url: string, params = {}) {
return await lastValueFrom(this._request(url, params, { method: 'GET', hideFromInspector: true })); return await lastValueFrom(this._request(url, params, { method: 'GET', hideFromInspector: true }));
} }
@ -728,7 +727,6 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
const params = data ? urlUtil.serializeParams(data) : ''; const params = data ? urlUtil.serializeParams(data) : '';
const url = `${this.instanceSettings.url}${apiUrl}${params.length ? `?${params}` : ''}`; const url = `${this.instanceSettings.url}${apiUrl}${params.length ? `?${params}` : ''}`;
const req = { ...options, url }; const req = { ...options, url };
return getBackendSrv().fetch(req); return getBackendSrv().fetch(req);
} }
@ -761,7 +759,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
const from = new Date(now); const from = new Date(now);
from.setMinutes(from.getMinutes() - 15); from.setMinutes(from.getMinutes() - 15);
observables.push( observables.push(
this.handleStreamingSearch( this.handleStreamingQuery(
{ {
range: { range: {
from: dateTime(from), from: dateTime(from),
@ -985,7 +983,7 @@ function errorAndDurationQuery(
throw new Error(getErrorMessage(errorRes.error?.message)); throw new Error(getErrorMessage(errorRes.error?.message));
} }
const serviceGraphView = getServiceGraphView( const serviceGraphView = getServiceGraphViewDataFrames(
request, request,
rateResponse, rateResponse,
errorAndDurationResponse[0], errorAndDurationResponse[0],
@ -1160,7 +1158,7 @@ function makePromServiceMapRequest(options: DataQueryRequest<TempoQuery>): DataQ
}; };
} }
function getServiceGraphView( function getServiceGraphViewDataFrames(
request: DataQueryRequest<TempoQuery>, request: DataQueryRequest<TempoQuery>,
rateResponse: ServiceMapQueryResponseWithRates, rateResponse: ServiceMapQueryResponseWithRates,
secondResponse: DataQueryResponse, secondResponse: DataQueryResponse,

View File

@ -1,7 +1,7 @@
import { DataFrameView, dateTime, createDataFrame, FieldType } from '@grafana/data'; import { DataFrameView, dateTime, createDataFrame, FieldType } from '@grafana/data';
import { createGraphFrames, mapPromMetricsToServiceMap } from './graphTransform'; import { createGraphFrames, mapPromMetricsToServiceMap } from './graphTransform';
import { bigResponse } from './testResponse'; import { bigResponse } from './test/testResponse';
describe('createGraphFrames', () => { describe('createGraphFrames', () => {
it('transforms basic response into nodes and edges frame', async () => { it('transforms basic response into nodes and edges frame', async () => {

View File

@ -14,7 +14,7 @@ import {
otlpDataFrameFromResponse, otlpDataFrameFromResponse,
otlpResponse, otlpResponse,
traceQlResponse, traceQlResponse,
} from './testResponse'; } from './test/testResponse';
import { TraceSearchMetadata } from './types'; import { TraceSearchMetadata } from './types';
const defaultSettings: DataSourceInstanceSettings = { const defaultSettings: DataSourceInstanceSettings = {

View File

@ -1,8 +1,8 @@
import { DataSourceInstanceSettings, PluginType, toUtc } from '@grafana/data'; import { DataSourceInstanceSettings, PluginType, toUtc } from '@grafana/data';
import { TemplateSrv } from '@grafana/runtime'; import { TemplateSrv } from '@grafana/runtime';
import { TempoDatasource } from './datasource'; import { TempoDatasource } from '../datasource';
import { TempoJsonData } from './types'; import { TempoJsonData } from '../types';
const rawRange = { const rawRange = {
from: toUtc('2018-04-25 10:00'), from: toUtc('2018-04-25 10:00'),

View File

@ -3,7 +3,7 @@ import { lastValueFrom } from 'rxjs';
import { DataQueryRequest, TimeRange } from '@grafana/data'; import { DataQueryRequest, TimeRange } from '@grafana/data';
import { TempoVariableQuery } from './VariableQueryEditor'; import { TempoVariableQuery } from './VariableQueryEditor';
import { createMetadataRequest, createTempoDatasource } from './mocks'; import { createMetadataRequest, createTempoDatasource } from './test/mocks';
import { TempoVariableSupport } from './variables'; import { TempoVariableSupport } from './variables';
describe('TempoVariableSupport', () => { describe('TempoVariableSupport', () => {