diff --git a/packages/grafana-prometheus/src/components/PromQueryField.tsx b/packages/grafana-prometheus/src/components/PromQueryField.tsx
index 4c619f88b7a..05cdda9864c 100644
--- a/packages/grafana-prometheus/src/components/PromQueryField.tsx
+++ b/packages/grafana-prometheus/src/components/PromQueryField.tsx
@@ -3,6 +3,7 @@ import { css, cx } from '@emotion/css';
import { PureComponent, ReactNode } from 'react';
import {
+ getDefaultTimeRange,
isDataFrame,
LocalStorageValueProvider,
QueryEditorProps,
@@ -249,6 +250,7 @@ class PromQueryFieldClass extends PureComponent
diff --git a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx
index df14c9e02c8..602aeca8f6a 100644
--- a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx
+++ b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.test.tsx
@@ -2,7 +2,7 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-import { createTheme } from '@grafana/data';
+import { createTheme, getDefaultTimeRange, TimeRange } from '@grafana/data';
import PromQlLanguageProvider from '../language_provider';
@@ -146,7 +146,7 @@ describe('PrometheusMetricsBrowser', () => {
const setupProps = (): BrowserProps => {
const mockLanguageProvider = {
start: () => Promise.resolve(),
- getLabelValues: (name: string) => {
+ getLabelValues: (timeRange: TimeRange, name: string) => {
switch (name) {
case 'label1':
return ['value1-1', 'value1-2'];
@@ -163,7 +163,7 @@ describe('PrometheusMetricsBrowser', () => {
// The metrics browser expects both label names and label values.
// The labels endpoint with match does not supply label values
// and so using it breaks the metrics browser.
- fetchSeriesLabels: (selector: string) => {
+ fetchSeriesLabels: (timeRange: TimeRange, selector: string) => {
switch (selector) {
case '{label1="value1-1"}':
return { label1: ['value1-1'], label2: ['value2-1'], label3: ['value3-1'] };
@@ -187,6 +187,7 @@ describe('PrometheusMetricsBrowser', () => {
lastUsedLabels: [],
storeLastUsedLabels: () => {},
deleteLastUsedLabels: () => {},
+ timeRange: getDefaultTimeRange(),
};
return defaults;
diff --git a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx
index 1eeb59d35ed..db3ec6c60b4 100644
--- a/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx
+++ b/packages/grafana-prometheus/src/components/PrometheusMetricsBrowser.tsx
@@ -4,7 +4,7 @@ import { ChangeEvent } from 'react';
import * as React from 'react';
import { FixedSizeList } from 'react-window';
-import { GrafanaTheme2, TimeRange } from '@grafana/data';
+import { getDefaultTimeRange, GrafanaTheme2, TimeRange } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import {
BrowserLabel as PromLabel,
@@ -409,11 +409,15 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component {
+ return this.props.timeRange ?? getDefaultTimeRange();
+ };
+
async fetchValues(name: string, selector: string) {
const { languageProvider } = this.props;
this.updateLabelState(name, { loading: true }, `Fetching values for ${name}`);
try {
- let rawValues = await languageProvider.getLabelValues(name);
+ let rawValues = await languageProvider.getLabelValues(this.getTimeRange(), name);
// If selector changed, clear loading state and discard result by returning early
if (selector !== buildSelector(this.state.labels)) {
this.updateLabelState(name, { loading: false });
@@ -444,7 +448,12 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component ({ label: variable, value: variable }));
+ let timeRange = range;
+ if (!timeRange) {
+ timeRange = getDefaultTimeRange();
+ }
+
if (!metric) {
// get all the labels
- datasource.getTagKeys({ filters: [] }).then((labelNames: Array<{ text: string }>) => {
+ datasource.getTagKeys({ timeRange, filters: [] }).then((labelNames: Array<{ text: string }>) => {
const names = labelNames.map(({ text }) => ({ label: text, value: text }));
setLabels(names, variables);
});
@@ -122,13 +127,15 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
const labelToConsider = [{ label: '__name__', op: '=', value: metric }];
const expr = promQueryModeller.renderLabels(labelToConsider);
- datasource.languageProvider.fetchLabelsWithMatch(expr).then((labelsIndex: Record) => {
- const labelNames = Object.keys(labelsIndex);
- const names = labelNames.map((value) => ({ label: value, value: value }));
- setLabels(names, variables);
- });
+ datasource.languageProvider
+ .fetchLabelsWithMatch(timeRange, expr)
+ .then((labelsIndex: Record) => {
+ const labelNames = Object.keys(labelsIndex);
+ const names = labelNames.map((value) => ({ label: value, value: value }));
+ setLabels(names, variables);
+ });
}
- }, [datasource, qryType, metric]);
+ }, [datasource, qryType, metric, range]);
const onChangeWithVariableString = (
updateVar: { [key: string]: QueryType | string },
@@ -302,6 +309,7 @@ export const PromVariableQueryEditor = ({ onChange, query, datasource, range }:
datasource={datasource}
onChange={metricsLabelsChange}
variableEditor={true}
+ timeRange={range ?? getDefaultTimeRange()}
/>
>
)}
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx
index 97e49df416b..d6c86499c25 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryField.tsx
@@ -105,7 +105,7 @@ const MonacoQueryField = (props: Props) => {
// we need only one instance of `overrideServices` during the lifetime of the react component
const overrideServicesRef = useRef(getOverrideServices());
const containerRef = useRef(null);
- const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, datasource } = props;
+ const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, datasource, timeRange } = props;
const lpRef = useLatest(languageProvider);
const historyRef = useLatest(history);
@@ -155,7 +155,7 @@ const MonacoQueryField = (props: Props) => {
historyProvider: historyRef.current,
languageProvider: lpRef.current,
});
- const completionProvider = getCompletionProvider(monaco, dataProvider);
+ const completionProvider = getCompletionProvider(monaco, dataProvider, timeRange);
// completion-providers in monaco are not registered directly to editor-instances,
// they are registered to languages. this makes it hard for us to have
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts
index 4ca3fd894ed..ac2995273e5 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/MonacoQueryFieldProps.ts
@@ -1,5 +1,5 @@
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryFieldProps.ts
-import { HistoryItem } from '@grafana/data';
+import { HistoryItem, TimeRange } from '@grafana/data';
import { PrometheusDatasource } from '../../datasource';
import type PromQlLanguageProvider from '../../language_provider';
@@ -17,4 +17,5 @@ export type Props = {
onRunQuery: (value: string) => void;
onBlur: (value: string) => void;
datasource: PrometheusDatasource;
+ timeRange: TimeRange;
};
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts
index 7a97ba98740..a778cf74300 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.test.ts
@@ -2,6 +2,7 @@ import { config } from '@grafana/runtime';
import { SUGGESTIONS_LIMIT } from '../../../language_provider';
import { FUNCTIONS } from '../../../promql';
+import { getMockTimeRange } from '../../../test/__mocks__/datasource';
import { filterMetricNames, getCompletions } from './completions';
import { DataProvider, type DataProviderParams } from './data_provider';
@@ -183,6 +184,8 @@ function getSuggestionCountForSituation(situationType: MetricNameSituation, metr
}
describe.each(metricNameCompletionSituations)('metric name completions in situation %s', (situationType) => {
+ const timeRange = getMockTimeRange();
+
it('should return completions for all metric names when the number of metric names is at or below the limit', async () => {
jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValue(metrics.atLimit);
const expectedCompletionsCount = getSuggestionCountForSituation(situationType, metrics.atLimit.length);
@@ -192,12 +195,12 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
// No text input
dataProvider.monacoSettings.setInputInRange('');
- let completions = await getCompletions(situation, dataProvider);
+ let completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(expectedCompletionsCount);
// With text input (use fuzzy search)
dataProvider.monacoSettings.setInputInRange('name_1');
- completions = await getCompletions(situation, dataProvider);
+ completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions?.length).toBeLessThanOrEqual(expectedCompletionsCount);
});
@@ -210,12 +213,12 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
// Complex query
dataProvider.monacoSettings.setInputInRange('metric name one two three four five');
- let completions = await getCompletions(situation, dataProvider);
+ let completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount);
// Simple query with fuzzy match
dataProvider.monacoSettings.setInputInRange('metric_name_');
- completions = await getCompletions(situation, dataProvider);
+ completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount);
});
@@ -227,19 +230,19 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
// Do not cross the metrics names threshold
jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValueOnce(metrics.atLimit);
dataProvider.monacoSettings.setInputInRange('name_1');
- await getCompletions(situation, dataProvider);
+ await getCompletions(situation, dataProvider, timeRange);
expect(dataProvider.monacoSettings.suggestionsIncomplete).toBe(false);
// Cross the metric names threshold, without text input
jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValueOnce(metrics.beyondLimit);
dataProvider.monacoSettings.setInputInRange('');
- await getCompletions(situation, dataProvider);
+ await getCompletions(situation, dataProvider, timeRange);
expect(dataProvider.monacoSettings.suggestionsIncomplete).toBe(true);
// Cross the metric names threshold, with text input
jest.spyOn(dataProvider, 'getAllMetricNames').mockReturnValueOnce(metrics.beyondLimit);
dataProvider.monacoSettings.setInputInRange('name_1');
- await getCompletions(situation, dataProvider);
+ await getCompletions(situation, dataProvider, timeRange);
expect(dataProvider.monacoSettings.suggestionsIncomplete).toBe(true);
});
@@ -253,7 +256,7 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
// Test with a complex query (> 4 terms)
dataProvider.monacoSettings.setInputInRange('metric name 1 with extra terms more');
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
const metricCompletions = completions.filter((c) => c.type === 'METRIC_NAME');
expect(metricCompletions.some((c) => c.label === 'metric_name_1_with_extra_terms')).toBe(true);
@@ -268,7 +271,7 @@ describe.each(metricNameCompletionSituations)('metric name completions in situat
// Test with multiple terms
dataProvider.monacoSettings.setInputInRange('metric name 1 2 3 4 5');
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
const expectedCompletionsCount = getSuggestionCountForSituation(situationType, metrics.beyondLimit.length);
expect(completions.length).toBeLessThanOrEqual(expectedCompletionsCount);
@@ -308,6 +311,8 @@ describe('Label value completions', () => {
});
});
+ const timeRange = getMockTimeRange();
+
it('should not escape special characters when between quotes', async () => {
const situation: Situation = {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
@@ -316,7 +321,7 @@ describe('Label value completions', () => {
otherLabels: [],
};
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(4);
expect(completions[0].insertText).toBe('value1');
@@ -333,7 +338,7 @@ describe('Label value completions', () => {
otherLabels: [],
};
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(4);
expect(completions[0].insertText).toBe('"value1"');
@@ -350,6 +355,8 @@ describe('Label value completions', () => {
});
});
+ const timeRange = getMockTimeRange();
+
it('should escape special characters when between quotes', async () => {
const situation: Situation = {
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
@@ -358,7 +365,7 @@ describe('Label value completions', () => {
otherLabels: [],
};
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(4);
expect(completions[0].insertText).toBe('value1');
@@ -375,7 +382,7 @@ describe('Label value completions', () => {
otherLabels: [],
};
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(4);
expect(completions[0].insertText).toBe('"value1"');
@@ -392,6 +399,8 @@ describe('Label value completions', () => {
});
});
+ const timeRange = getMockTimeRange();
+
it('should handle empty values', async () => {
jest.spyOn(dataProvider, 'getLabelValues').mockResolvedValue(['']);
@@ -402,7 +411,7 @@ describe('Label value completions', () => {
otherLabels: [],
};
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(1);
expect(completions[0].insertText).toBe('""');
});
@@ -417,7 +426,7 @@ describe('Label value completions', () => {
otherLabels: [],
};
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(1);
expect(completions[0].insertText).toBe('test\\"\\\\value');
});
@@ -432,7 +441,7 @@ describe('Label value completions', () => {
otherLabels: [],
};
- const completions = await getCompletions(situation, dataProvider);
+ const completions = await getCompletions(situation, dataProvider, timeRange);
expect(completions).toHaveLength(1);
expect(completions[0].insertText).toBe('"123"');
});
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts
index ff1f0490560..af0b51bddd4 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/completions.ts
@@ -2,6 +2,7 @@
import UFuzzy from '@leeoniya/ufuzzy';
import { languages } from 'monaco-editor';
+import { TimeRange } from '@grafana/data';
import { config } from '@grafana/runtime';
import { prometheusRegularEscape } from '../../../datasource';
@@ -160,14 +161,15 @@ function makeSelector(metricName: string | undefined, labels: Label[]): string {
async function getLabelNames(
metric: string | undefined,
otherLabels: Label[],
- dataProvider: DataProvider
+ dataProvider: DataProvider,
+ timeRange: TimeRange
): Promise {
if (metric === undefined && otherLabels.length === 0) {
// if there is no filtering, we have to use a special endpoint
return Promise.resolve(dataProvider.getAllLabelNames());
} else {
const selector = makeSelector(metric, otherLabels);
- return await dataProvider.getSeriesLabels(selector, otherLabels);
+ return await dataProvider.getSeriesLabels(timeRange, selector, otherLabels);
}
}
@@ -176,9 +178,10 @@ async function getLabelNamesForCompletions(
suffix: string,
triggerOnInsert: boolean,
otherLabels: Label[],
- dataProvider: DataProvider
+ dataProvider: DataProvider,
+ timeRange: TimeRange
): Promise {
- const labelNames = await getLabelNames(metric, otherLabels, dataProvider);
+ const labelNames = await getLabelNames(metric, otherLabels, dataProvider, timeRange);
return labelNames.map((text) => {
const isUtf8 = !isValidLegacyName(text);
return {
@@ -200,31 +203,34 @@ async function getLabelNamesForCompletions(
async function getLabelNamesForSelectorCompletions(
metric: string | undefined,
otherLabels: Label[],
- dataProvider: DataProvider
+ dataProvider: DataProvider,
+ timeRange: TimeRange
): Promise {
- return getLabelNamesForCompletions(metric, '=', true, otherLabels, dataProvider);
+ return getLabelNamesForCompletions(metric, '=', true, otherLabels, dataProvider, timeRange);
}
async function getLabelNamesForByCompletions(
metric: string | undefined,
otherLabels: Label[],
- dataProvider: DataProvider
+ dataProvider: DataProvider,
+ timeRange: TimeRange
): Promise {
- return getLabelNamesForCompletions(metric, '', false, otherLabels, dataProvider);
+ return getLabelNamesForCompletions(metric, '', false, otherLabels, dataProvider, timeRange);
}
async function getLabelValues(
metric: string | undefined,
labelName: string,
otherLabels: Label[],
- dataProvider: DataProvider
+ dataProvider: DataProvider,
+ timeRange: TimeRange
): Promise {
if (metric === undefined && otherLabels.length === 0) {
// if there is no filtering, we have to use a special endpoint
- return dataProvider.getLabelValues(labelName);
+ return dataProvider.getLabelValues(timeRange, labelName);
} else {
const selector = makeSelector(metric, otherLabels);
- return await dataProvider.getSeriesValues(labelName, selector);
+ return await dataProvider.getSeriesValues(timeRange, labelName, selector);
}
}
@@ -233,9 +239,10 @@ async function getLabelValuesForMetricCompletions(
labelName: string,
betweenQuotes: boolean,
otherLabels: Label[],
- dataProvider: DataProvider
+ dataProvider: DataProvider,
+ timeRange: TimeRange
): Promise {
- const values = await getLabelValues(metric, labelName, otherLabels, dataProvider);
+ const values = await getLabelValues(metric, labelName, otherLabels, dataProvider, timeRange);
return values.map((text) => ({
type: 'LABEL_VALUE',
label: text,
@@ -248,7 +255,11 @@ function formatLabelValueForCompletion(value: string, betweenQuotes: boolean): s
return betweenQuotes ? text : `"${text}"`;
}
-export function getCompletions(situation: Situation, dataProvider: DataProvider): Promise {
+export function getCompletions(
+ situation: Situation,
+ dataProvider: DataProvider,
+ timeRange: TimeRange
+): Promise {
switch (situation.type) {
case 'IN_DURATION':
return Promise.resolve(DURATION_COMPLETIONS);
@@ -263,16 +274,17 @@ export function getCompletions(situation: Situation, dataProvider: DataProvider)
return Promise.resolve([...historyCompletions, ...FUNCTION_COMPLETIONS, ...metricNames]);
}
case 'IN_LABEL_SELECTOR_NO_LABEL_NAME':
- return getLabelNamesForSelectorCompletions(situation.metricName, situation.otherLabels, dataProvider);
+ return getLabelNamesForSelectorCompletions(situation.metricName, situation.otherLabels, dataProvider, timeRange);
case 'IN_GROUPING':
- return getLabelNamesForByCompletions(situation.metricName, situation.otherLabels, dataProvider);
+ return getLabelNamesForByCompletions(situation.metricName, situation.otherLabels, dataProvider, timeRange);
case 'IN_LABEL_SELECTOR_WITH_LABEL_NAME':
return getLabelValuesForMetricCompletions(
situation.metricName,
situation.labelName,
situation.betweenQuotes,
situation.otherLabels,
- dataProvider
+ dataProvider,
+ timeRange
);
default:
throw new NeverCaseError(situation);
diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts
index 5108401b2fd..def57a02621 100644
--- a/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts
+++ b/packages/grafana-prometheus/src/components/monaco-query-field/monaco-completion-provider/index.ts
@@ -1,4 +1,5 @@
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/index.ts
+import { TimeRange } from '@grafana/data';
import type { Monaco, monacoTypes } from '@grafana/ui';
import { CompletionType, getCompletions } from './completions';
@@ -48,7 +49,8 @@ function getMonacoCompletionItemKind(type: CompletionType, monaco: Monaco): mona
export function getCompletionProvider(
monaco: Monaco,
- dataProvider: DataProvider
+ dataProvider: DataProvider,
+ timeRange: TimeRange
): monacoTypes.languages.CompletionItemProvider {
const provideCompletionItems = (
model: monacoTypes.editor.ITextModel,
@@ -84,7 +86,8 @@ export function getCompletionProvider(
const offset = model.getOffsetAt(positionClone);
const situation = getSituation(model.getValue(), offset);
- const completionsPromise = situation != null ? getCompletions(situation, dataProvider) : Promise.resolve([]);
+ const completionsPromise =
+ situation != null ? getCompletions(situation, dataProvider, timeRange) : Promise.resolve([]);
return completionsPromise.then((items) => {
// monaco by-default alphabetically orders the items.
diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts
index 44e93ebb7d1..082e391ad9b 100644
--- a/packages/grafana-prometheus/src/datasource.ts
+++ b/packages/grafana-prometheus/src/datasource.ts
@@ -483,7 +483,7 @@ export class PrometheusDatasource
return Promise.resolve([]);
}
- const timeRange = options?.range ?? this.languageProvider.timeRange ?? getDefaultTimeRange();
+ const timeRange = options?.range ?? getDefaultTimeRange();
const scopedVars = {
...this.getIntervalVars(),
@@ -654,6 +654,10 @@ export class PrometheusDatasource
// it is used in metric_find_query.ts
// and in Tempo here grafana/public/app/plugins/datasource/tempo/QueryEditor/ServiceGraphSection.tsx
async getTagKeys(options: DataSourceGetTagKeysOptions): Promise {
+ if (!options.timeRange) {
+ options.timeRange = getDefaultTimeRange();
+ }
+
if (config.featureToggles.promQLScope && (options?.scopes?.length ?? 0) > 0) {
const suggestions = await this.languageProvider.fetchSuggestions(
options.timeRange,
@@ -680,7 +684,10 @@ export class PrometheusDatasource
}));
const expr = promQueryModeller.renderLabels(labelFilters);
- let labelsIndex: Record = await this.languageProvider.fetchLabelsWithMatch(expr);
+ let labelsIndex: Record = await this.languageProvider.fetchLabelsWithMatch(
+ options.timeRange,
+ expr
+ );
// filter out already used labels
return Object.keys(labelsIndex)
@@ -689,7 +696,11 @@ export class PrometheusDatasource
}
// By implementing getTagKeys and getTagValues we add ad-hoc filters functionality
- async getTagValues(options: DataSourceGetTagValuesOptions) {
+ async getTagValues(options: DataSourceGetTagValuesOptions): Promise {
+ if (!options.timeRange) {
+ options.timeRange = getDefaultTimeRange();
+ }
+
const requestId = `[${this.uid}][${options.key}]`;
if (config.featureToggles.promQLScope && (options?.scopes?.length ?? 0) > 0) {
return (
@@ -715,7 +726,7 @@ export class PrometheusDatasource
if (this.hasLabelsMatchAPISupport()) {
return (
- await this.languageProvider.fetchSeriesValuesWithMatch(options.key, expr, requestId, options.timeRange)
+ await this.languageProvider.fetchSeriesValuesWithMatch(options.timeRange, options.key, expr, requestId)
).map((v) => ({
value: v,
text: v,
diff --git a/packages/grafana-prometheus/src/language_provider.test.ts b/packages/grafana-prometheus/src/language_provider.test.ts
index 2b0c7edcaf3..e91845d1f00 100644
--- a/packages/grafana-prometheus/src/language_provider.test.ts
+++ b/packages/grafana-prometheus/src/language_provider.test.ts
@@ -110,6 +110,8 @@ describe('Language completion provider', () => {
});
describe('getSeriesLabels', () => {
+ const timeRange = getMockTimeRange();
+
it('should call labels endpoint', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
@@ -120,7 +122,7 @@ describe('Language completion provider', () => {
const labelName = 'job';
const labelValue = 'grafana';
- getSeriesLabels(`{${labelName}="${labelValue}"}`, [
+ getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [
{
name: labelName,
value: labelValue,
@@ -151,7 +153,7 @@ describe('Language completion provider', () => {
const labelName = 'job';
const labelValue = 'grafana';
- getSeriesLabels(`{${labelName}="${labelValue}"}`, [
+ getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [
{
name: labelName,
value: labelValue,
@@ -186,7 +188,7 @@ describe('Language completion provider', () => {
const labelName = 'job';
const labelValue = 'grafana';
- getSeriesLabels(`{${labelName}="${labelValue}"}`, [
+ getSeriesLabels(timeRange, `{${labelName}="${labelValue}"}`, [
{
name: labelName,
value: labelValue,
@@ -217,13 +219,15 @@ describe('Language completion provider', () => {
});
describe('getSeriesValues', () => {
+ const timeRange = getMockTimeRange();
+
it('should call old series endpoint and should use match[] parameter', () => {
const languageProvider = new LanguageProvider({
...defaultDatasource,
} as PrometheusDatasource);
const getSeriesValues = languageProvider.getSeriesValues;
const requestSpy = jest.spyOn(languageProvider, 'request');
- getSeriesValues('job', '{job="grafana"}');
+ getSeriesValues(timeRange, 'job', '{job="grafana"}');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/series',
@@ -246,7 +250,7 @@ describe('Language completion provider', () => {
const requestSpy = jest.spyOn(languageProvider, 'request');
const labelName = 'job';
const labelValue = 'grafana';
- getSeriesValues(labelName, `{${labelName}="${labelValue}"}`);
+ getSeriesValues(timeRange, labelName, `{${labelName}="${labelValue}"}`);
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
`/api/v1/label/${labelName}/values`,
@@ -267,7 +271,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const getSeriesValues = languageProvider.getSeriesValues;
const requestSpy = jest.spyOn(languageProvider, 'request');
- getSeriesValues('job', '{instance="$instance", job="grafana"}');
+ getSeriesValues(timeRange, 'job', '{instance="$instance", job="grafana"}');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/series',
@@ -288,7 +292,7 @@ describe('Language completion provider', () => {
const timeRange = getMockTimeRange();
await languageProvider.start(timeRange);
const requestSpy = jest.spyOn(languageProvider, 'request');
- await languageProvider.fetchSeries('{job="grafana"}');
+ await languageProvider.fetchSeries(timeRange, '{job="grafana"}');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/series',
@@ -311,7 +315,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
const requestSpy = jest.spyOn(languageProvider, 'request');
- fetchSeriesLabels('$metric');
+ fetchSeriesLabels(getMockTimeRange(), '$metric');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/series',
@@ -332,7 +336,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
const requestSpy = jest.spyOn(languageProvider, 'request');
- fetchSeriesLabels('metric-with-limit', undefined, 'none');
+ fetchSeriesLabels(getMockTimeRange(), 'metric-with-limit', undefined, 'none');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/series',
@@ -353,7 +357,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const fetchSeriesLabels = languageProvider.fetchSeriesLabels;
const requestSpy = jest.spyOn(languageProvider, 'request');
- fetchSeriesLabels('metric-without-limit', false, 'none');
+ fetchSeriesLabels(getMockTimeRange(), 'metric-without-limit', false, 'none');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/series',
@@ -619,7 +623,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const fetchLabelValues = languageProvider.fetchLabelValues;
const requestSpy = jest.spyOn(languageProvider, 'request');
- fetchLabelValues('$job');
+ fetchLabelValues(getMockTimeRange(), '$job');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/interpolated_job/values',
@@ -639,7 +643,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const fetchLabelValues = languageProvider.fetchLabelValues;
const requestSpy = jest.spyOn(languageProvider, 'request');
- fetchLabelValues('"http.status:sum"');
+ fetchLabelValues(getMockTimeRange(), '"http.status:sum"');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/U__http_2e_status:sum/values',
@@ -661,7 +665,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch;
const requestSpy = jest.spyOn(languageProvider, 'request');
- fetchSeriesValuesWithMatch('"http.status:sum"', '{__name__="a_utf8_http_requests_total"}');
+ fetchSeriesValuesWithMatch(getMockTimeRange(), '"http.status:sum"', '{__name__="a_utf8_http_requests_total"}');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/U__http_2e_status:sum/values',
@@ -681,7 +685,7 @@ describe('Language completion provider', () => {
} as PrometheusDatasource);
const fetchSeriesValuesWithMatch = languageProvider.fetchSeriesValuesWithMatch;
const requestSpy = jest.spyOn(languageProvider, 'request');
- fetchSeriesValuesWithMatch('"http_status_sum"', '{__name__="a_utf8_http_requests_total"}');
+ fetchSeriesValuesWithMatch(getMockTimeRange(), '"http_status_sum"', '{__name__="a_utf8_http_requests_total"}');
expect(requestSpy).toHaveBeenCalled();
expect(requestSpy).toHaveBeenCalledWith(
'/api/v1/label/http_status_sum/values',
diff --git a/packages/grafana-prometheus/src/language_provider.ts b/packages/grafana-prometheus/src/language_provider.ts
index 22c427c979b..04a9f7e6e7b 100644
--- a/packages/grafana-prometheus/src/language_provider.ts
+++ b/packages/grafana-prometheus/src/language_provider.ts
@@ -79,7 +79,6 @@ const PREFIX_DELIMITER_REGEX =
const secondsInDay = 86400;
export default class PromQlLanguageProvider extends LanguageProvider {
histogramMetrics: string[];
- timeRange: TimeRange;
metrics: string[];
metricsMetadata?: PromMetricsMetadata;
declare startTask: Promise;
@@ -92,7 +91,6 @@ export default class PromQlLanguageProvider extends LanguageProvider {
this.datasource = datasource;
this.histogramMetrics = [];
- this.timeRange = getDefaultTimeRange();
this.metrics = [];
Object.assign(this, initialValues);
@@ -109,7 +107,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
cleanText(s: string) {
const parts = s.split(PREFIX_DELIMITER_REGEX);
const last = parts.pop()!;
- return last.trimLeft().replace(/"$/, '').replace(/^"/, '');
+ return last.trimStart().replace(/"$/, '').replace(/^"/, '');
}
get syntax() {
@@ -129,16 +127,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
return defaultValue;
};
- start = async (timeRange?: TimeRange): Promise => {
- this.timeRange = timeRange ?? getDefaultTimeRange();
-
+ start = async (timeRange: TimeRange = getDefaultTimeRange()): Promise => {
if (this.datasource.lookupsDisabled) {
return [];
}
- this.metrics = (await this.fetchLabelValues('__name__')) || [];
+ this.metrics = (await this.fetchLabelValues(timeRange, '__name__')) || [];
this.histogramMetrics = processHistogramMetrics(this.metrics).sort();
- return Promise.all([this.loadMetricsMetadata(), this.fetchLabels()]);
+ return Promise.all([this.loadMetricsMetadata(), this.fetchLabels(timeRange)]);
};
async loadMetricsMetadata() {
@@ -186,15 +182,15 @@ export default class PromQlLanguageProvider extends LanguageProvider {
};
}
- async getSeries(selector: string, withName?: boolean): Promise> {
+ async getSeries(timeRange: TimeRange, selector: string, withName?: boolean): Promise> {
if (this.datasource.lookupsDisabled) {
return {};
}
try {
if (selector === EMPTY_SELECTOR) {
- return await this.fetchDefaultSeries();
+ return await this.fetchDefaultSeries(timeRange);
} else {
- return await this.fetchSeriesLabels(selector, withName, REMOVE_SERIES_LIMIT);
+ return await this.fetchSeriesLabels(timeRange, selector, withName, REMOVE_SERIES_LIMIT);
}
} catch (error) {
// TODO: better error handling
@@ -203,11 +199,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
}
}
- /**
- * @param key
- */
- fetchLabelValues = async (key: string): Promise => {
- const params = this.datasource.getAdjustedInterval(this.timeRange);
+ fetchLabelValues = async (range: TimeRange, key: string): Promise => {
+ const params = this.datasource.getAdjustedInterval(range);
const interpolatedName = this.datasource.interpolateString(key);
const interpolatedAndEscapedName = escapeForUtf8Support(removeQuotesIfExist(interpolatedName));
const url = `/api/v1/label/${interpolatedAndEscapedName}/values`;
@@ -215,19 +208,16 @@ export default class PromQlLanguageProvider extends LanguageProvider {
return value ?? [];
};
- async getLabelValues(key: string): Promise {
- return await this.fetchLabelValues(key);
+ async getLabelValues(range: TimeRange, key: string): Promise {
+ return await this.fetchLabelValues(range, key);
}
/**
* Fetches all label keys
*/
- fetchLabels = async (timeRange?: TimeRange, queries?: PromQuery[]): Promise => {
- if (timeRange) {
- this.timeRange = timeRange;
- }
+ fetchLabels = async (timeRange: TimeRange, queries?: PromQuery[]): Promise => {
let url = '/api/v1/labels';
- const timeParams = this.datasource.getAdjustedInterval(this.timeRange);
+ const timeParams = this.datasource.getAdjustedInterval(timeRange);
this.labelFetchTs = Date.now().valueOf();
const searchParams = new URLSearchParams({ ...timeParams });
@@ -259,17 +249,15 @@ export default class PromQlLanguageProvider extends LanguageProvider {
/**
* Gets series values
- * Function to replace old getSeries calls in a way that will provide faster endpoints for new prometheus instances,
- * while maintaining backward compatability
- * @param labelName
- * @param selector
+ * Function to replace old getSeries calls in a way that will provide faster endpoints
+ * for new prometheus instances, while maintaining backward compatability
*/
- getSeriesValues = async (labelName: string, selector: string): Promise => {
+ getSeriesValues = async (timeRange: TimeRange, labelName: string, selector: string): Promise => {
if (!this.datasource.hasLabelsMatchAPISupport()) {
- const data = await this.getSeries(selector);
+ const data = await this.getSeries(timeRange, selector);
return data[removeQuotesIfExist(labelName)] ?? [];
}
- return await this.fetchSeriesValuesWithMatch(labelName, selector);
+ return await this.fetchSeriesValuesWithMatch(timeRange, labelName, selector);
};
/**
@@ -280,10 +268,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
* @param requestId
*/
fetchSeriesValuesWithMatch = async (
+ timeRange: TimeRange,
name: string,
match: string,
- requestId?: string,
- timeRange: TimeRange = this.timeRange
+ requestId?: string
): Promise => {
const interpolatedName = name ? this.datasource.interpolateString(name) : null;
const interpolatedMatch = match ? this.datasource.interpolateString(match) : null;
@@ -321,16 +309,16 @@ export default class PromQlLanguageProvider extends LanguageProvider {
* @param selector
* @param otherLabels
*/
- getSeriesLabels = async (selector: string, otherLabels: Label[]): Promise => {
+ getSeriesLabels = async (timeRange: TimeRange, selector: string, otherLabels: Label[]): Promise => {
let possibleLabelNames, data: Record;
if (!this.datasource.hasLabelsMatchAPISupport()) {
- data = await this.getSeries(selector);
+ data = await this.getSeries(timeRange, selector);
possibleLabelNames = Object.keys(data); // all names from prometheus
} else {
// Exclude __name__ from output
otherLabels.push({ name: '__name__', value: '', op: '!=' });
- data = await this.fetchSeriesLabelsMatch(selector);
+ data = await this.fetchSeriesLabelsMatch(timeRange, selector);
possibleLabelNames = Object.keys(data);
}
@@ -341,31 +329,31 @@ export default class PromQlLanguageProvider extends LanguageProvider {
/**
* Fetch labels using the best endpoint that datasource supports.
* This is cached by its args but also by the global timeRange currently selected as they can change over requested time.
- * @param name
- * @param withName
*/
- fetchLabelsWithMatch = async (name: string, withName?: boolean): Promise> => {
+ fetchLabelsWithMatch = async (
+ timeRange: TimeRange,
+ name: string,
+ withName?: boolean
+ ): Promise> => {
if (this.datasource.hasLabelsMatchAPISupport()) {
- return this.fetchSeriesLabelsMatch(name, withName);
+ return this.fetchSeriesLabelsMatch(timeRange, name, withName);
} else {
- return this.fetchSeriesLabels(name, withName, REMOVE_SERIES_LIMIT);
+ return this.fetchSeriesLabels(timeRange, name, withName, REMOVE_SERIES_LIMIT);
}
};
/**
* Fetch labels for a series using /series endpoint. This is cached by its args but also by the global timeRange currently selected as
* they can change over requested time.
- * @param name
- * @param withName
- * @param withLimit
*/
fetchSeriesLabels = async (
+ timeRange: TimeRange,
name: string,
withName?: boolean,
withLimit?: string
): Promise> => {
const interpolatedName = this.datasource.interpolateString(name);
- const range = this.datasource.getAdjustedInterval(this.timeRange);
+ const range = this.datasource.getAdjustedInterval(timeRange);
let urlParams: UrlParamsType = {
...range,
'match[]': interpolatedName,
@@ -385,12 +373,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
/**
* Fetch labels for a series using /labels endpoint. This is cached by its args but also by the global timeRange currently selected as
* they can change over requested time.
- * @param name
- * @param withName
*/
- fetchSeriesLabelsMatch = async (name: string, withName?: boolean): Promise> => {
+ fetchSeriesLabelsMatch = async (
+ timeRange: TimeRange,
+ name: string,
+ withName?: boolean
+ ): Promise> => {
const interpolatedName = this.datasource.interpolateString(name);
- const range = this.datasource.getAdjustedInterval(this.timeRange);
+ const range = this.datasource.getAdjustedInterval(timeRange);
const urlParams = {
...range,
'match[]': interpolatedName,
@@ -404,11 +394,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
/**
* Fetch series for a selector. Use this for raw results. Use fetchSeriesLabels() to get labels.
- * @param match
*/
- fetchSeries = async (match: string): Promise>> => {
+ fetchSeries = async (timeRange: TimeRange, match: string): Promise>> => {
const url = '/api/v1/series';
- const range = this.datasource.getTimeRangeParams(this.timeRange);
+ const range = this.datasource.getTimeRangeParams(timeRange);
const params = { ...range, 'match[]': match };
return await this.request(url, {}, params, this.getDefaultCacheHeaders());
};
@@ -418,8 +407,8 @@ export default class PromQlLanguageProvider extends LanguageProvider {
* because we can cache more aggressively here and also we do not want to invalidate this cache the same way as in
* fetchSeriesLabels.
*/
- fetchDefaultSeries = once(async () => {
- const values = await Promise.all(DEFAULT_KEYS.map((key) => this.fetchLabelValues(key)));
+ fetchDefaultSeries = once(async (timeRange: TimeRange) => {
+ const values = await Promise.all(DEFAULT_KEYS.map((key) => this.fetchLabelValues(timeRange, key)));
return DEFAULT_KEYS.reduce((acc, key, i) => ({ ...acc, [key]: values[i] }), {});
});
@@ -442,12 +431,12 @@ export default class PromQlLanguageProvider extends LanguageProvider {
limit?: number,
requestId?: string
): Promise => {
- if (timeRange) {
- this.timeRange = timeRange;
+ if (!timeRange) {
+ timeRange = getDefaultTimeRange();
}
const url = '/suggestions';
- const timeParams = this.datasource.getAdjustedInterval(this.timeRange);
+ const timeParams = this.datasource.getAdjustedInterval(timeRange);
const value = await this.request(
url,
[],
@@ -456,7 +445,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
queries: queries?.map((q) =>
this.datasource.interpolateString(q.expr, {
...this.datasource.getIntervalVars(),
- ...this.datasource.getRangeScopedVars(this.timeRange),
+ ...this.datasource.getRangeScopedVars(timeRange),
})
),
scopes: scopes?.reduce((acc, scope) => {
diff --git a/packages/grafana-prometheus/src/metric_find_query.ts b/packages/grafana-prometheus/src/metric_find_query.ts
index 69494f0f7fc..12d17e7126f 100644
--- a/packages/grafana-prometheus/src/metric_find_query.ts
+++ b/packages/grafana-prometheus/src/metric_find_query.ts
@@ -1,7 +1,7 @@
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/metric_find_query.ts
import { chain, map as _map, uniq } from 'lodash';
-import { getDefaultTimeRange, MetricFindValue, TimeRange } from '@grafana/data';
+import { MetricFindValue, TimeRange } from '@grafana/data';
import { PrometheusDatasource } from './datasource';
import { getPrometheusTime } from './language_utils';
@@ -15,19 +15,15 @@ import {
import { escapeForUtf8Support, isValidLegacyName } from './utf8_support';
export class PrometheusMetricFindQuery {
- range: TimeRange;
-
constructor(
private datasource: PrometheusDatasource,
private query: string
) {
this.datasource = datasource;
this.query = query;
- this.range = getDefaultTimeRange();
}
process(timeRange: TimeRange): Promise {
- this.range = timeRange;
const labelNamesRegex = PrometheusLabelNamesRegex;
const labelNamesRegexWithMatch = PrometheusLabelNamesRegexWithMatch;
const labelValuesRegex = PrometheusLabelValuesRegex;
@@ -38,7 +34,7 @@ export class PrometheusMetricFindQuery {
if (labelNamesMatchQuery) {
const selector = `{__name__=~".*${labelNamesMatchQuery[1]}.*"}`;
- return this.datasource.languageProvider.getSeriesLabels(selector, []).then((results) =>
+ return this.datasource.languageProvider.getSeriesLabels(timeRange, selector, []).then((results) =>
results.map((result) => ({
text: result,
}))
@@ -54,35 +50,35 @@ export class PrometheusMetricFindQuery {
const filter = labelValuesQuery[1];
const label = labelValuesQuery[2];
if (isFilterDefined(filter)) {
- return this.labelValuesQuery(label, filter);
+ return this.labelValuesQuery(label, timeRange, filter);
} else {
// Exclude the filter part of the expression because it is blank or empty
- return this.labelValuesQuery(label);
+ return this.labelValuesQuery(label, timeRange);
}
}
const metricNamesQuery = this.query.match(metricNamesRegex);
if (metricNamesQuery) {
- return this.metricNameQuery(metricNamesQuery[1]);
+ return this.metricNameQuery(metricNamesQuery[1], timeRange);
}
const queryResultQuery = this.query.match(queryResultRegex);
if (queryResultQuery) {
- return this.queryResultQuery(queryResultQuery[1]);
+ return this.queryResultQuery(queryResultQuery[1], timeRange);
}
// if query contains full metric name, return metric name and label list
const expressions = ['label_values()', 'metrics()', 'query_result()'];
if (!expressions.includes(this.query)) {
- return this.metricNameAndLabelsQuery(this.query);
+ return this.metricNameAndLabelsQuery(this.query, timeRange);
}
return Promise.resolve([]);
}
- labelValuesQuery(label: string, metric?: string) {
- const start = getPrometheusTime(this.range.from, false);
- const end = getPrometheusTime(this.range.to, true);
+ labelValuesQuery(label: string, range: TimeRange, metric?: string) {
+ const start = getPrometheusTime(range.from, false);
+ const end = getPrometheusTime(range.to, true);
const params = { ...(metric && { 'match[]': metric }), start: start.toString(), end: end.toString() };
let escapedLabel = label;
@@ -118,9 +114,9 @@ export class PrometheusMetricFindQuery {
}
}
- metricNameQuery(metricFilterPattern: string) {
- const start = getPrometheusTime(this.range.from, false);
- const end = getPrometheusTime(this.range.to, true);
+ metricNameQuery(metricFilterPattern: string, range: TimeRange) {
+ const start = getPrometheusTime(range.from, false);
+ const end = getPrometheusTime(range.to, true);
const params = {
start: start.toString(),
end: end.toString(),
@@ -143,11 +139,11 @@ export class PrometheusMetricFindQuery {
});
}
- queryResultQuery(query: string) {
+ queryResultQuery(query: string, range: TimeRange) {
const url = '/api/v1/query';
const params = {
query,
- time: getPrometheusTime(this.range.to, true).toString(),
+ time: getPrometheusTime(range.to, true).toString(),
};
return this.datasource.metadataRequest(url, params).then((result) => {
switch (result.data.data.resultType) {
@@ -182,9 +178,9 @@ export class PrometheusMetricFindQuery {
});
}
- metricNameAndLabelsQuery(query: string): Promise {
- const start = getPrometheusTime(this.range.from, false);
- const end = getPrometheusTime(this.range.to, true);
+ metricNameAndLabelsQuery(query: string, range: TimeRange): Promise {
+ const start = getPrometheusTime(range.from, false);
+ const end = getPrometheusTime(range.to, true);
const params = {
'match[]': query,
start: start.toString(),
diff --git a/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx b/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx
index ae6353e8b1a..f6f62ffb54b 100644
--- a/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx
+++ b/packages/grafana-prometheus/src/querybuilder/components/MetricsLabelsSection.tsx
@@ -1,7 +1,7 @@
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/MetricsLabelsSection.tsx
import { useCallback } from 'react';
-import { SelectableValue } from '@grafana/data';
+import { SelectableValue, TimeRange } from '@grafana/data';
import { config } from '@grafana/runtime';
import { PrometheusDatasource } from '../../datasource';
@@ -22,6 +22,7 @@ export interface MetricsLabelsSectionProps {
onChange: (update: PromVisualQuery) => void;
variableEditor?: boolean;
onBlur?: () => void;
+ timeRange: TimeRange;
}
export function MetricsLabelsSection({
@@ -30,6 +31,7 @@ export function MetricsLabelsSection({
onChange,
onBlur,
variableEditor,
+ timeRange,
}: MetricsLabelsSectionProps) {
// fixing the use of 'as' from refactoring
// @ts-ignore
@@ -63,7 +65,7 @@ export function MetricsLabelsSection({
const onGetLabelNames = async (forLabel: Partial): Promise => {
// If no metric we need to use a different method
if (!query.metric) {
- await datasource.languageProvider.fetchLabels();
+ await datasource.languageProvider.fetchLabels(timeRange);
return datasource.languageProvider.getLabelKeys().map((k) => ({ value: k }));
}
@@ -71,7 +73,7 @@ export function MetricsLabelsSection({
labelsToConsider.push({ label: '__name__', op: '=', value: query.metric });
const expr = promQueryModeller.renderLabels(labelsToConsider);
- let labelsIndex: Record = await datasource.languageProvider.fetchLabelsWithMatch(expr);
+ let labelsIndex: Record = await datasource.languageProvider.fetchLabelsWithMatch(timeRange, expr);
// filter out already used labels
return Object.keys(labelsIndex)
@@ -124,7 +126,7 @@ export function MetricsLabelsSection({
if (!forLabel.label) {
return Promise.resolve([]);
}
- const result = datasource.languageProvider.fetchSeries(promQLExpression);
+ const result = datasource.languageProvider.fetchSeries(timeRange, promQLExpression);
const forLabelInterpolated = datasource.interpolateString(forLabel.label);
return result.then((result) => {
// This query returns duplicate values, scrub them out
@@ -154,7 +156,7 @@ export function MetricsLabelsSection({
const requestId = `[${datasource.uid}][${query.metric}][${forLabel.label}][${forLabel.op}]`;
return datasource.languageProvider
- .fetchSeriesValuesWithMatch(forLabel.label, promQLExpression, requestId)
+ .fetchSeriesValuesWithMatch(timeRange, forLabel.label, promQLExpression, requestId)
.then((response) => response.map((v) => ({ value: v, label: v })));
};
@@ -169,7 +171,7 @@ export function MetricsLabelsSection({
}
// If no metric is selected, we can get the raw list of labels
if (!query.metric) {
- return (await datasource.languageProvider.getLabelValues(forLabel.label)).map((v) => ({ value: v }));
+ return (await datasource.languageProvider.getLabelValues(timeRange, forLabel.label)).map((v) => ({ value: v }));
}
const labelsToConsider = query.labels.filter((x) => x !== forLabel);
@@ -191,8 +193,8 @@ export function MetricsLabelsSection({
};
const onGetMetrics = useCallback(() => {
- return withTemplateVariableOptions(getMetrics(datasource, query));
- }, [datasource, query, withTemplateVariableOptions]);
+ return withTemplateVariableOptions(getMetrics(datasource, query, timeRange));
+ }, [datasource, query, timeRange, withTemplateVariableOptions]);
const MetricSelectComponent = config.featureToggles.prometheusUsesCombobox ? MetricCombobox : MetricSelect;
@@ -229,7 +231,8 @@ export function MetricsLabelsSection({
*/
async function getMetrics(
datasource: PrometheusDatasource,
- query: PromVisualQuery
+ query: PromVisualQuery,
+ timeRange: TimeRange
): Promise> {
// Makes sure we loaded the metadata for metrics. Usually this is done in the start() method of the provider but we
// don't use it with the visual builder and there is no need to run all the start() setup anyway.
@@ -245,9 +248,9 @@ async function getMetrics(
let metrics: string[];
if (query.labels.length > 0) {
const expr = promQueryModeller.renderLabels(query.labels);
- metrics = (await datasource.languageProvider.getSeries(expr, true))['__name__'] ?? [];
+ metrics = (await datasource.languageProvider.getSeries(timeRange, expr, true))['__name__'] ?? [];
} else {
- metrics = (await datasource.languageProvider.getLabelValues('__name__')) ?? [];
+ metrics = (await datasource.languageProvider.getLabelValues(timeRange, '__name__')) ?? [];
}
return metrics.map((m) => ({
diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
index 8e58f495963..372873eebb1 100644
--- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
+++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx
@@ -89,7 +89,7 @@ describe('PromQueryBuilder', () => {
it('tries to load metrics without labels', async () => {
const { languageProvider, container } = setup();
await openMetricSelect(container);
- await waitFor(() => expect(languageProvider.getLabelValues).toHaveBeenCalledWith('__name__'));
+ await waitFor(() => expect(languageProvider.getLabelValues).toHaveBeenCalledWith(expect.anything(), '__name__'));
});
it('tries to load metrics with labels', async () => {
@@ -98,7 +98,13 @@ describe('PromQueryBuilder', () => {
labels: [{ label: 'label_name', op: '=', value: 'label_value' }],
});
await openMetricSelect(container);
- await waitFor(() => expect(languageProvider.getSeries).toHaveBeenCalledWith('{label_name="label_value"}', true));
+ await waitFor(() =>
+ expect(languageProvider.getSeries).toHaveBeenCalledWith(
+ expect.anything(),
+ '{label_name="label_value"}',
+ expect.anything()
+ )
+ );
});
it('tries to load variables in metric field', async () => {
@@ -113,7 +119,10 @@ describe('PromQueryBuilder', () => {
const { languageProvider } = setup();
await openLabelNameSelect();
await waitFor(() =>
- expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith('{__name__="random_metric"}')
+ expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+ expect.anything(),
+ '{__name__="random_metric"}'
+ )
);
});
@@ -135,6 +144,7 @@ describe('PromQueryBuilder', () => {
await openLabelNameSelect(1);
await waitFor(() =>
expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+ expect.anything(),
'{label_name="label_value", __name__="random_metric"}'
)
);
@@ -273,7 +283,10 @@ describe('PromQueryBuilder', () => {
});
await openLabelNameSelect();
await waitFor(() =>
- expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith('{__name__="random_metric"}')
+ expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+ expect.anything(),
+ '{__name__="random_metric"}'
+ )
);
});
@@ -301,6 +314,7 @@ describe('PromQueryBuilder', () => {
await openLabelNameSelect(1);
await waitFor(() =>
expect(languageProvider.fetchLabelsWithMatch).toHaveBeenCalledWith(
+ expect.anything(),
'{label_name="label_value", __name__="random_metric"}'
)
);
diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx
index 11c79f5932c..a7e0841f634 100644
--- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx
+++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx
@@ -2,7 +2,7 @@
import { css } from '@emotion/css';
import { memo, useState } from 'react';
-import { DataSourceApi, PanelData } from '@grafana/data';
+import { DataSourceApi, getDefaultTimeRange, PanelData } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { EditorRow } from '@grafana/plugin-ui';
@@ -43,7 +43,12 @@ export const PromQueryBuilder = memo((props) => {
return (
<>
-
+
{initHints.length ? (