mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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>
This commit is contained in:
@@ -4,7 +4,7 @@ import { act } from 'react-dom/test-utils';
|
||||
import PromExploreQueryEditor from './PromExploreQueryEditor';
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
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 datasourceMock: unknown = {};
|
||||
@@ -12,6 +12,14 @@ const setup = (renderMethod: any, propOverrides?: object) => {
|
||||
const onRunQuery = jest.fn();
|
||||
const onChange = jest.fn();
|
||||
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 = {
|
||||
state: LoadingState.NotStarted,
|
||||
series: [],
|
||||
@@ -50,6 +58,7 @@ const setup = (renderMethod: any, propOverrides?: object) => {
|
||||
const props: any = {
|
||||
query,
|
||||
data,
|
||||
range,
|
||||
datasource,
|
||||
exploreMode,
|
||||
history,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { PromExploreExtraField } from './PromExploreExtraField';
|
||||
export type Props = ExploreQueryFieldProps<PrometheusDatasource, PromQuery, PromOptions>;
|
||||
|
||||
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) {
|
||||
const { query, onChange } = props;
|
||||
@@ -49,6 +49,7 @@ export const PromExploreQueryEditor: FC<Props> = (props: Props) => {
|
||||
<PromQueryField
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
range={range}
|
||||
onRunQuery={onRunQuery}
|
||||
onChange={onChange}
|
||||
onBlur={() => {}}
|
||||
|
||||
@@ -96,7 +96,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { datasource, query, data } = this.props;
|
||||
const { datasource, query, range, data } = this.props;
|
||||
const { formatOption, instant, interval, intervalFactorOption, legendFormat } = this.state;
|
||||
|
||||
return (
|
||||
@@ -104,6 +104,7 @@ export class PromQueryEditor extends PureComponent<Props, State> {
|
||||
<PromQueryField
|
||||
datasource={datasource}
|
||||
query={query}
|
||||
range={range}
|
||||
onRunQuery={this.onRunQuery}
|
||||
onChange={this.onFieldChange}
|
||||
history={[]}
|
||||
|
||||
@@ -17,7 +17,14 @@ import Prism from 'prismjs';
|
||||
// dom also includes Element polyfills
|
||||
import { PromQuery, PromOptions, PromMetricsMetadata } from '../types';
|
||||
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 { PrometheusDatasource } from '../datasource';
|
||||
|
||||
@@ -163,9 +170,24 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
const {
|
||||
data,
|
||||
datasource: { languageProvider },
|
||||
range,
|
||||
} = 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,5 +57,15 @@ exports[`PromExploreQueryEditor should render component 1`] = `
|
||||
"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', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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', () => {
|
||||
let query: any;
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Libraries
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import LRU from 'lru-cache';
|
||||
// Services & Utils
|
||||
import {
|
||||
AnnotationEvent,
|
||||
@@ -66,7 +67,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
directUrl: string;
|
||||
basicAuth: any;
|
||||
withCredentials: any;
|
||||
metricsNameCache: any;
|
||||
metricsNameCache = new LRU<string, string[]>(10);
|
||||
interval: string;
|
||||
queryTimeout: string;
|
||||
httpMethod: string;
|
||||
@@ -527,20 +528,6 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
|
||||
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) {
|
||||
if (!query) {
|
||||
return Promise.resolve([]);
|
||||
|
||||
@@ -114,7 +114,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
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.metricsMetadata = fixSummariesMetadata(await this.request('/api/v1/metadata', {}));
|
||||
this.processHistogramMetrics(this.metrics);
|
||||
@@ -404,7 +411,13 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
}
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ describe('PrometheusMetricFindQuery', () => {
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchMock).toHaveBeenCalledWith({
|
||||
method: 'GET',
|
||||
url: 'proxied/api/v1/labels',
|
||||
url: `proxied/api/v1/labels?start=${raw.from.unix()}&end=${raw.to.unix()}`,
|
||||
hideFromInspector: true,
|
||||
headers: {},
|
||||
});
|
||||
@@ -95,7 +95,7 @@ describe('PrometheusMetricFindQuery', () => {
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchMock).toHaveBeenCalledWith({
|
||||
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,
|
||||
headers: {},
|
||||
});
|
||||
@@ -190,7 +190,7 @@ describe('PrometheusMetricFindQuery', () => {
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(fetchMock).toHaveBeenCalledWith({
|
||||
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,
|
||||
headers: {},
|
||||
});
|
||||
|
||||
@@ -48,7 +48,15 @@ export default class PrometheusMetricFindQuery {
|
||||
}
|
||||
|
||||
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 _.map(result.data.data, value => {
|
||||
return { text: value };
|
||||
@@ -57,11 +65,18 @@ export default class PrometheusMetricFindQuery {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!metric) {
|
||||
const params = new URLSearchParams({
|
||||
start: start.toString(),
|
||||
end: end.toString(),
|
||||
});
|
||||
// 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 _.map(result.data.data, value => {
|
||||
@@ -69,8 +84,6 @@ export default class PrometheusMetricFindQuery {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const start = this.datasource.getPrometheusTime(this.range.from, false);
|
||||
const end = this.datasource.getPrometheusTime(this.range.to, true);
|
||||
const params = new URLSearchParams({
|
||||
'match[]': metric,
|
||||
start: start.toString(),
|
||||
@@ -96,7 +109,13 @@ export default class PrometheusMetricFindQuery {
|
||||
}
|
||||
|
||||
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 _.chain(result.data.data)
|
||||
|
||||
Reference in New Issue
Block a user