Commit 0c703088 by Kemal Akkoyun Committed by GitHub

Prometheus: Add time range parameters to labels API (#27548)

* Add time range parameters to labels API

Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>

* Fix minor issues

Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>

* Add range to explore component

Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>

* Add range to query component

Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>

* Cache metric names for time range

Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>

* Update public/app/plugins/datasource/prometheus/components/PromQueryField.tsx

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* Remove unused method

Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>

* Only compare the ranges

Signed-off-by: Kemal Akkoyun <kakkoyun@gmail.com>

* Update public/app/plugins/datasource/prometheus/components/PromQueryField.tsx

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
parent 5c1c5f41
...@@ -4,7 +4,7 @@ import { act } from 'react-dom/test-utils'; ...@@ -4,7 +4,7 @@ import { act } from 'react-dom/test-utils';
import PromExploreQueryEditor from './PromExploreQueryEditor'; import PromExploreQueryEditor from './PromExploreQueryEditor';
import { PrometheusDatasource } from '../datasource'; import { PrometheusDatasource } from '../datasource';
import { PromQuery } from '../types'; import { PromQuery } from '../types';
import { LoadingState, PanelData, toUtc } from '@grafana/data'; import { LoadingState, PanelData, toUtc, TimeRange } from '@grafana/data';
const setup = (renderMethod: any, propOverrides?: object) => { const setup = (renderMethod: any, propOverrides?: object) => {
const datasourceMock: unknown = {}; const datasourceMock: unknown = {};
...@@ -12,6 +12,14 @@ const setup = (renderMethod: any, propOverrides?: object) => { ...@@ -12,6 +12,14 @@ const setup = (renderMethod: any, propOverrides?: object) => {
const onRunQuery = jest.fn(); const onRunQuery = jest.fn();
const onChange = jest.fn(); const onChange = jest.fn();
const query: PromQuery = { expr: '', refId: 'A', interval: '1s' }; const query: PromQuery = { expr: '', refId: 'A', interval: '1s' };
const range: TimeRange = {
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
raw: {
from: toUtc('2020-01-01', 'YYYY-MM-DD'),
to: toUtc('2020-01-02', 'YYYY-MM-DD'),
},
};
const data: PanelData = { const data: PanelData = {
state: LoadingState.NotStarted, state: LoadingState.NotStarted,
series: [], series: [],
...@@ -50,6 +58,7 @@ const setup = (renderMethod: any, propOverrides?: object) => { ...@@ -50,6 +58,7 @@ const setup = (renderMethod: any, propOverrides?: object) => {
const props: any = { const props: any = {
query, query,
data, data,
range,
datasource, datasource,
exploreMode, exploreMode,
history, history,
......
...@@ -12,7 +12,7 @@ import { PromExploreExtraField } from './PromExploreExtraField'; ...@@ -12,7 +12,7 @@ import { PromExploreExtraField } from './PromExploreExtraField';
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>; export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
export const PromExploreQueryEditor: FC<Props> = (props: Props) => { export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
const { query, data, datasource, history, onChange, onRunQuery } = props; const { range, query, data, datasource, history, onChange, onRunQuery } = props;
function onChangeQueryStep(value: string) { function onChangeQueryStep(value: string) {
const { query, onChange } = props; const { query, onChange } = props;
...@@ -49,6 +49,7 @@ export const PromExploreQueryEditor: FC<Props> = (props: Props) => { ...@@ -49,6 +49,7 @@ export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
<PromQueryField <PromQueryField
datasource={datasource} datasource={datasource}
query={query} query={query}
range={range}
onRunQuery={onRunQuery} onRunQuery={onRunQuery}
onChange={onChange} onChange={onChange}
onBlur={() => {}} onBlur={() => {}}
......
...@@ -96,7 +96,7 @@ export class PromQueryEditor extends PureComponent<Props, State> { ...@@ -96,7 +96,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
}; };
render() { render() {
const { datasource, query, data } = this.props; const { datasource, query, range, data } = this.props;
const { formatOption, instant, interval, intervalFactorOption, legendFormat } = this.state; const { formatOption, instant, interval, intervalFactorOption, legendFormat } = this.state;
return ( return (
...@@ -104,6 +104,7 @@ export class PromQueryEditor extends PureComponent<Props, State> { ...@@ -104,6 +104,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
<PromQueryField <PromQueryField
datasource={datasource} datasource={datasource}
query={query} query={query}
range={range}
onRunQuery={this.onRunQuery} onRunQuery={this.onRunQuery}
onChange={this.onFieldChange} onChange={this.onFieldChange}
history={[]} history={[]}
......
...@@ -17,7 +17,14 @@ import Prism from 'prismjs'; ...@@ -17,7 +17,14 @@ import Prism from 'prismjs';
// dom also includes Element polyfills // dom also includes Element polyfills
import { PromQuery, PromOptions, PromMetricsMetadata } from '../types'; import { PromQuery, PromOptions, PromMetricsMetadata } from '../types';
import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise'; import { CancelablePromise, makePromiseCancelable } from 'app/core/utils/CancelablePromise';
import { ExploreQueryFieldProps, QueryHint, isDataFrame, toLegacyResponseData, HistoryItem } from '@grafana/data'; import {
ExploreQueryFieldProps,
QueryHint,
isDataFrame,
toLegacyResponseData,
HistoryItem,
AbsoluteTimeRange,
} from '@grafana/data';
import { DOMUtil, SuggestionsState } from '@grafana/ui'; import { DOMUtil, SuggestionsState } from '@grafana/ui';
import { PrometheusDatasource } from '../datasource'; import { PrometheusDatasource } from '../datasource';
...@@ -163,9 +170,24 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF ...@@ -163,9 +170,24 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
const { const {
data, data,
datasource: { languageProvider }, datasource: { languageProvider },
range,
} = this.props; } = this.props;
if (languageProvider !== prevProps.datasource.languageProvider) { let refreshed = false;
if (range && prevProps.range) {
const absoluteRange: AbsoluteTimeRange = { from: range.from.valueOf(), to: range.to.valueOf() };
const prevAbsoluteRange: AbsoluteTimeRange = {
from: prevProps.range.from.valueOf(),
to: prevProps.range.to.valueOf(),
};
if (!_.isEqual(absoluteRange, prevAbsoluteRange)) {
this.refreshMetrics();
refreshed = true;
}
}
if (!refreshed && languageProvider !== prevProps.datasource.languageProvider) {
this.refreshMetrics(); this.refreshMetrics();
} }
......
...@@ -57,5 +57,15 @@ exports[`PromExploreQueryEditor should render component 1`] = ` ...@@ -57,5 +57,15 @@ exports[`PromExploreQueryEditor should render component 1`] = `
"refId": "A", "refId": "A",
} }
} }
range={
Object {
"from": "2020-01-01T00:00:00.000Z",
"raw": Object {
"from": "2020-01-01T00:00:00.000Z",
"to": "2020-01-02T00:00:00.000Z",
},
"to": "2020-01-02T00:00:00.000Z",
}
}
/> />
`; `;
...@@ -203,26 +203,6 @@ describe('PrometheusDatasource', () => { ...@@ -203,26 +203,6 @@ describe('PrometheusDatasource', () => {
}); });
}); });
describe('When performing performSuggestQuery', () => {
it('should cache response', async () => {
fetchMock.mockImplementation(() =>
of({
status: 'success',
data: { data: ['value1', 'value2', 'value3'] },
})
);
let results = await ds.performSuggestQuery('value', true);
expect(results).toHaveLength(3);
fetchMock.mockImplementation(jest.fn());
results = await ds.performSuggestQuery('value', true);
expect(results).toHaveLength(3);
});
});
describe('When converting prometheus histogram to heatmap format', () => { describe('When converting prometheus histogram to heatmap format', () => {
let query: any; let query: any;
beforeEach(() => { beforeEach(() => {
......
// Libraries // Libraries
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import LRU from 'lru-cache';
// Services & Utils // Services & Utils
import { import {
AnnotationEvent, AnnotationEvent,
...@@ -66,7 +67,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -66,7 +67,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
directUrl: string; directUrl: string;
basicAuth: any; basicAuth: any;
withCredentials: any; withCredentials: any;
metricsNameCache: any; metricsNameCache = new LRU<string, string[]>(10);
interval: string; interval: string;
queryTimeout: string; queryTimeout: string;
httpMethod: string; httpMethod: string;
...@@ -527,20 +528,6 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -527,20 +528,6 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
return error; return error;
}; };
async performSuggestQuery(query: string, cache = false) {
if (cache && this.metricsNameCache?.expire > Date.now()) {
return this.metricsNameCache.data.filter((metricName: any) => metricName.indexOf(query) !== 1);
}
const response: PromLabelQueryResponse = await this.metadataRequest('/api/v1/label/__name__/values');
this.metricsNameCache = {
data: response.data.data,
expire: Date.now() + 60 * 1000,
};
return response.data.data.filter(metricName => metricName.indexOf(query) !== 1);
}
metricFindQuery(query: string) { metricFindQuery(query: string) {
if (!query) { if (!query) {
return Promise.resolve([]); return Promise.resolve([]);
......
...@@ -114,7 +114,14 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -114,7 +114,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
return []; return [];
} }
this.metrics = await this.request('/api/v1/label/__name__/values', []); const tRange = this.datasource.getTimeRange();
const params = new URLSearchParams({
start: tRange['start'].toString(),
end: tRange['end'].toString(),
});
const url = `/api/v1/label/__name__/values?${params.toString()}`;
this.metrics = await this.request(url, []);
this.lookupsDisabled = this.metrics.length > this.lookupMetricsThreshold; this.lookupsDisabled = this.metrics.length > this.lookupMetricsThreshold;
this.metricsMetadata = fixSummariesMetadata(await this.request('/api/v1/metadata', {})); this.metricsMetadata = fixSummariesMetadata(await this.request('/api/v1/metadata', {}));
this.processHistogramMetrics(this.metrics); this.processHistogramMetrics(this.metrics);
...@@ -404,7 +411,13 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -404,7 +411,13 @@ export default class PromQlLanguageProvider extends LanguageProvider {
} }
fetchLabelValues = async (key: string): Promise<Record<string, string[]>> => { fetchLabelValues = async (key: string): Promise<Record<string, string[]>> => {
const data = await this.request(`/api/v1/label/${key}/values`, []); const tRange = this.datasource.getTimeRange();
const params = new URLSearchParams({
start: tRange['start'].toString(),
end: tRange['end'].toString(),
});
const url = `/api/v1/label/${key}/values?${params.toString()}`;
const data = await this.request(url, []);
return { [key]: data }; return { [key]: data };
}; };
......
...@@ -76,7 +76,7 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -76,7 +76,7 @@ describe('PrometheusMetricFindQuery', () => {
expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: 'proxied/api/v1/labels', url: `proxied/api/v1/labels?start=${raw.from.unix()}&end=${raw.to.unix()}`,
hideFromInspector: true, hideFromInspector: true,
headers: {}, headers: {},
}); });
...@@ -95,7 +95,7 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -95,7 +95,7 @@ describe('PrometheusMetricFindQuery', () => {
expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: 'proxied/api/v1/label/resource/values', url: `proxied/api/v1/label/resource/values?start=${raw.from.unix()}&end=${raw.to.unix()}`,
hideFromInspector: true, hideFromInspector: true,
headers: {}, headers: {},
}); });
...@@ -190,7 +190,7 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -190,7 +190,7 @@ describe('PrometheusMetricFindQuery', () => {
expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: 'proxied/api/v1/label/__name__/values', url: `proxied/api/v1/label/__name__/values?start=${raw.from.unix()}&end=${raw.to.unix()}`,
hideFromInspector: true, hideFromInspector: true,
headers: {}, headers: {},
}); });
......
...@@ -48,7 +48,15 @@ export default class PrometheusMetricFindQuery { ...@@ -48,7 +48,15 @@ export default class PrometheusMetricFindQuery {
} }
labelNamesQuery() { labelNamesQuery() {
const url = '/api/v1/labels'; const start = this.datasource.getPrometheusTime(this.range.from, false);
const end = this.datasource.getPrometheusTime(this.range.to, true);
const params = new URLSearchParams({
start: start.toString(),
end: end.toString(),
});
const url = `/api/v1/labels?${params.toString()}`;
return this.datasource.metadataRequest(url).then((result: any) => { return this.datasource.metadataRequest(url).then((result: any) => {
return _.map(result.data.data, value => { return _.map(result.data.data, value => {
return { text: value }; return { text: value };
...@@ -57,11 +65,18 @@ export default class PrometheusMetricFindQuery { ...@@ -57,11 +65,18 @@ export default class PrometheusMetricFindQuery {
} }
labelValuesQuery(label: string, metric?: string) { labelValuesQuery(label: string, metric?: string) {
const start = this.datasource.getPrometheusTime(this.range.from, false);
const end = this.datasource.getPrometheusTime(this.range.to, true);
let url: string; let url: string;
if (!metric) { if (!metric) {
const params = new URLSearchParams({
start: start.toString(),
end: end.toString(),
});
// return label values globally // return label values globally
url = '/api/v1/label/' + label + '/values'; url = `/api/v1/label/${label}/values?${params.toString()}`;
return this.datasource.metadataRequest(url).then((result: any) => { return this.datasource.metadataRequest(url).then((result: any) => {
return _.map(result.data.data, value => { return _.map(result.data.data, value => {
...@@ -69,8 +84,6 @@ export default class PrometheusMetricFindQuery { ...@@ -69,8 +84,6 @@ export default class PrometheusMetricFindQuery {
}); });
}); });
} else { } else {
const start = this.datasource.getPrometheusTime(this.range.from, false);
const end = this.datasource.getPrometheusTime(this.range.to, true);
const params = new URLSearchParams({ const params = new URLSearchParams({
'match[]': metric, 'match[]': metric,
start: start.toString(), start: start.toString(),
...@@ -96,7 +109,13 @@ export default class PrometheusMetricFindQuery { ...@@ -96,7 +109,13 @@ export default class PrometheusMetricFindQuery {
} }
metricNameQuery(metricFilterPattern: string) { metricNameQuery(metricFilterPattern: string) {
const url = '/api/v1/label/__name__/values'; const start = this.datasource.getPrometheusTime(this.range.from, false);
const end = this.datasource.getPrometheusTime(this.range.to, true);
const params = new URLSearchParams({
start: start.toString(),
end: end.toString(),
});
const url = `/api/v1/label/__name__/values?${params.toString()}`;
return this.datasource.metadataRequest(url).then((result: any) => { return this.datasource.metadataRequest(url).then((result: any) => {
return _.chain(result.data.data) return _.chain(result.data.data)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment