Commit a73e6f72 by Hugo Häggmark Committed by GitHub

Prometheus: Use fetch instead of deprecated datasourceRequest (#27090)

* Prometheus: replaces dataSourcRequest with fetch

* Tests: fixes typings

* Refactor: removes unnecessary from operator

* Refactor: removes weird json() function calls

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Update public/app/plugins/datasource/prometheus/metric_find_query.test.ts

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>
parent 0fb7ee05
...@@ -18,13 +18,13 @@ import { ...@@ -18,13 +18,13 @@ import {
TimeRange, TimeRange,
TimeSeries, TimeSeries,
} from '@grafana/data'; } from '@grafana/data';
import { forkJoin, from, merge, Observable, of } from 'rxjs'; import { forkJoin, merge, Observable, of, throwError } from 'rxjs';
import { filter, map, tap } from 'rxjs/operators'; import { catchError, filter, map, tap } from 'rxjs/operators';
import PrometheusMetricFindQuery from './metric_find_query'; import PrometheusMetricFindQuery from './metric_find_query';
import { ResultTransformer } from './result_transformer'; import { ResultTransformer } from './result_transformer';
import PrometheusLanguageProvider from './language_provider'; import PrometheusLanguageProvider from './language_provider';
import { getBackendSrv, BackendSrvRequest } from '@grafana/runtime'; import { BackendSrvRequest, getBackendSrv } from '@grafana/runtime';
import addLabelToQuery from './add_label_to_query'; import addLabelToQuery from './add_label_to_query';
import { getQueryHints } from './query_hints'; import { getQueryHints } from './query_hints';
import { expandRecordingRules } from './language_utils'; import { expandRecordingRules } from './language_utils';
...@@ -111,7 +111,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -111,7 +111,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
} }
} }
_request(url: string, data: Record<string, string> | null, overrides: Partial<BackendSrvRequest> = {}) { _request<T = any>(url: string, data: Record<string, string> | null, overrides: Partial<BackendSrvRequest> = {}) {
const options: BackendSrvRequest = defaults(overrides, { const options: BackendSrvRequest = defaults(overrides, {
url: this.url + url, url: this.url + url,
method: this.httpMethod, method: this.httpMethod,
...@@ -140,12 +140,12 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -140,12 +140,12 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
options.headers!.Authorization = this.basicAuth; options.headers!.Authorization = this.basicAuth;
} }
return getBackendSrv().datasourceRequest(options); return getBackendSrv().fetch<T>(options);
} }
// Use this for tab completion features, wont publish response to other components // Use this for tab completion features, wont publish response to other components
metadataRequest(url: string) { metadataRequest<T = any>(url: string) {
return this._request(url, null, { method: 'GET', hideFromInspector: true }); return this._request<T>(url, null, { method: 'GET', hideFromInspector: true }).toPromise(); // toPromise until we change getTagValues, getTagKeys to Observable
} }
interpolateQueryExpr(value: string | string[] = [], variable: any) { interpolateQueryExpr(value: string | string[] = [], variable: any) {
...@@ -272,8 +272,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -272,8 +272,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
const target = activeTargets[index]; const target = activeTargets[index];
let observable = query.instant let observable = query.instant
? from(this.performInstantQuery(query, end)) ? this.performInstantQuery(query, end)
: from(this.performTimeSeriesQuery(query, query.start, query.end)); : this.performTimeSeriesQuery(query, query.start, query.end);
return observable.pipe( return observable.pipe(
// Decrease the counter here. We assume that each request returns only single value and then completes // Decrease the counter here. We assume that each request returns only single value and then completes
...@@ -305,8 +305,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -305,8 +305,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
const target = activeTargets[index]; const target = activeTargets[index];
let observable = query.instant let observable = query.instant
? from(this.performInstantQuery(query, end)) ? this.performInstantQuery(query, end)
: from(this.performTimeSeriesQuery(query, query.start, query.end)); : this.performTimeSeriesQuery(query, query.start, query.end);
return observable.pipe( return observable.pipe(
filter((response: any) => (response.cancelled ? false : true)), filter((response: any) => (response.cancelled ? false : true)),
...@@ -448,13 +448,15 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -448,13 +448,15 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
} }
} }
return this._request(url, data, { requestId: query.requestId, headers: query.headers }).catch((err: any) => { return this._request(url, data, { requestId: query.requestId, headers: query.headers }).pipe(
if (err.cancelled) { catchError(err => {
return err; if (err.cancelled) {
} return of(err);
}
throw this.handleErrors(err, query); return throwError(this.handleErrors(err, query));
}); })
);
} }
performInstantQuery(query: PromQueryRequest, time: number) { performInstantQuery(query: PromQueryRequest, time: number) {
...@@ -474,13 +476,15 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -474,13 +476,15 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
} }
} }
return this._request(url, data, { requestId: query.requestId, headers: query.headers }).catch((err: any) => { return this._request(url, data, { requestId: query.requestId, headers: query.headers }).pipe(
if (err.cancelled) { catchError(err => {
return err; if (err.cancelled) {
} return of(err);
}
throw this.handleErrors(err, query); return throwError(this.handleErrors(err, query));
}); })
);
} }
handleErrors = (err: any, target: PromQuery) => { handleErrors = (err: any, target: PromQuery) => {
...@@ -582,7 +586,11 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -582,7 +586,11 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
const query = this.createQuery(queryModel, queryOptions, start, end); const query = this.createQuery(queryModel, queryOptions, start, end);
const self = this; const self = this;
const response: PromDataQueryResponse = await this.performTimeSeriesQuery(query, query.start, query.end); const response: PromDataQueryResponse = await this.performTimeSeriesQuery(
query,
query.start,
query.end
).toPromise();
const eventList: AnnotationEvent[] = []; const eventList: AnnotationEvent[] = [];
const splitKeys = tagKeys.split(','); const splitKeys = tagKeys.split(',');
...@@ -662,7 +670,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -662,7 +670,7 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
async testDatasource() { async testDatasource() {
const now = new Date().getTime(); const now = new Date().getTime();
const query = { expr: '1+1' } as PromQueryRequest; const query = { expr: '1+1' } as PromQueryRequest;
const response = await this.performInstantQuery(query, now / 1000); const response = await this.performInstantQuery(query, now / 1000).toPromise();
return response.data.status === 'success' return response.data.status === 'success'
? { status: 'success', message: 'Data source is working' } ? { status: 'success', message: 'Data source is working' }
: { status: 'error', message: response.error }; : { status: 'error', message: response.error };
...@@ -690,9 +698,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions> ...@@ -690,9 +698,8 @@ export class PrometheusDatasource extends DataSourceApi<PromQuery, PromOptions>
async loadRules() { async loadRules() {
try { try {
const res = await this.metadataRequest('/api/v1/rules'); const res = await this.metadataRequest('/api/v1/rules');
const body = res.data || res.json(); const groups = res.data?.data?.groups;
const groups = body?.data?.groups;
if (groups) { if (groups) {
this.ruleMappings = extractRuleMappingFromGroups(groups); this.ruleMappings = extractRuleMappingFromGroups(groups);
} }
......
...@@ -2,14 +2,14 @@ import _ from 'lodash'; ...@@ -2,14 +2,14 @@ import _ from 'lodash';
import LRU from 'lru-cache'; import LRU from 'lru-cache';
import { Value } from 'slate'; import { Value } from 'slate';
import { dateTime, LanguageProvider, HistoryItem } from '@grafana/data'; import { dateTime, HistoryItem, LanguageProvider } from '@grafana/data';
import { CompletionItem, TypeaheadInput, TypeaheadOutput, CompletionItemGroup } from '@grafana/ui'; import { CompletionItem, CompletionItemGroup, TypeaheadInput, TypeaheadOutput } from '@grafana/ui';
import { parseSelector, processLabels, processHistogramLabels, fixSummariesMetadata } from './language_utils'; import { fixSummariesMetadata, parseSelector, processHistogramLabels, processLabels } from './language_utils';
import PromqlSyntax, { FUNCTIONS, RATE_RANGES } from './promql'; import PromqlSyntax, { FUNCTIONS, RATE_RANGES } from './promql';
import { PrometheusDatasource } from './datasource'; import { PrometheusDatasource } from './datasource';
import { PromQuery, PromMetricsMetadata } from './types'; import { PromMetricsMetadata, PromQuery } from './types';
const DEFAULT_KEYS = ['job', 'instance']; const DEFAULT_KEYS = ['job', 'instance'];
const EMPTY_SELECTOR = '{}'; const EMPTY_SELECTOR = '{}';
...@@ -101,9 +101,7 @@ export default class PromQlLanguageProvider extends LanguageProvider { ...@@ -101,9 +101,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
request = async (url: string, defaultValue: any): Promise<any> => { request = async (url: string, defaultValue: any): Promise<any> => {
try { try {
const res = await this.datasource.metadataRequest(url); const res = await this.datasource.metadataRequest(url);
const body = await (res.data || res.json()); return res.data.data;
return body.data;
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
......
import 'whatwg-fetch'; // fetch polyfill needed backendSrv
import { of } from 'rxjs';
import { DataSourceInstanceSettings, toUtc } from '@grafana/data';
import { PrometheusDatasource } from './datasource'; import { PrometheusDatasource } from './datasource';
import PrometheusMetricFindQuery from './metric_find_query'; import PrometheusMetricFindQuery from './metric_find_query';
import { DataSourceInstanceSettings, toUtc } from '@grafana/data';
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__ import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
import { PromOptions } from './types'; import { PromOptions } from './types';
import { FetchResponse } from '@grafana/runtime';
jest.mock('app/features/templating/template_srv', () => { jest.mock('app/features/templating/template_srv', () => {
return { return {
...@@ -16,7 +20,7 @@ jest.mock('@grafana/runtime', () => ({ ...@@ -16,7 +20,7 @@ jest.mock('@grafana/runtime', () => ({
getBackendSrv: () => backendSrv, getBackendSrv: () => backendSrv,
})); }));
const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest'); const fetchMock = jest.spyOn(backendSrv, 'fetch');
const instanceSettings = ({ const instanceSettings = ({
url: 'proxied', url: 'proxied',
...@@ -54,7 +58,7 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -54,7 +58,7 @@ describe('PrometheusMetricFindQuery', () => {
}); });
const setupMetricFindQuery = (data: any) => { const setupMetricFindQuery = (data: any) => {
datasourceRequestMock.mockImplementation(() => Promise.resolve({ status: 'success', data: data.response })); fetchMock.mockImplementation(() => of(({ status: 'success', data: data.response } as unknown) as FetchResponse));
return new PrometheusMetricFindQuery(ds, data.query); return new PrometheusMetricFindQuery(ds, data.query);
}; };
...@@ -69,8 +73,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -69,8 +73,8 @@ describe('PrometheusMetricFindQuery', () => {
const results = await query.process(); const results = await query.process();
expect(results).toHaveLength(3); expect(results).toHaveLength(3);
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: 'proxied/api/v1/labels', url: 'proxied/api/v1/labels',
hideFromInspector: true, hideFromInspector: true,
...@@ -88,8 +92,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -88,8 +92,8 @@ describe('PrometheusMetricFindQuery', () => {
const results = await query.process(); const results = await query.process();
expect(results).toHaveLength(3); expect(results).toHaveLength(3);
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: 'proxied/api/v1/label/resource/values', url: 'proxied/api/v1/label/resource/values',
hideFromInspector: true, hideFromInspector: true,
...@@ -111,8 +115,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -111,8 +115,8 @@ describe('PrometheusMetricFindQuery', () => {
const results = await query.process(); const results = await query.process();
expect(results).toHaveLength(3); expect(results).toHaveLength(3);
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: `proxied/api/v1/series?match${encodeURIComponent( url: `proxied/api/v1/series?match${encodeURIComponent(
'[]' '[]'
...@@ -136,8 +140,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -136,8 +140,8 @@ describe('PrometheusMetricFindQuery', () => {
const results = await query.process(); const results = await query.process();
expect(results).toHaveLength(3); expect(results).toHaveLength(3);
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: url:
'proxied/api/v1/series?match%5B%5D=metric%7Blabel1%3D%22foo%22%2C+label2%3D%22bar%22%2C+label3%3D%22baz%22%7D&start=1524650400&end=1524654000', 'proxied/api/v1/series?match%5B%5D=metric%7Blabel1%3D%22foo%22%2C+label2%3D%22bar%22%2C+label3%3D%22baz%22%7D&start=1524650400&end=1524654000',
...@@ -162,8 +166,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -162,8 +166,8 @@ describe('PrometheusMetricFindQuery', () => {
expect(results).toHaveLength(2); expect(results).toHaveLength(2);
expect(results[0].text).toBe('value1'); expect(results[0].text).toBe('value1');
expect(results[1].text).toBe('value2'); expect(results[1].text).toBe('value2');
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: `proxied/api/v1/series?match${encodeURIComponent( url: `proxied/api/v1/series?match${encodeURIComponent(
'[]' '[]'
...@@ -183,8 +187,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -183,8 +187,8 @@ describe('PrometheusMetricFindQuery', () => {
const results = await query.process(); const results = await query.process();
expect(results).toHaveLength(3); expect(results).toHaveLength(3);
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: 'proxied/api/v1/label/__name__/values', url: 'proxied/api/v1/label/__name__/values',
hideFromInspector: true, hideFromInspector: true,
...@@ -211,8 +215,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -211,8 +215,8 @@ describe('PrometheusMetricFindQuery', () => {
expect(results).toHaveLength(1); expect(results).toHaveLength(1);
expect(results[0].text).toBe('metric{job="testjob"} 3846 1443454528000'); expect(results[0].text).toBe('metric{job="testjob"} 3846 1443454528000');
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: `proxied/api/v1/query?query=metric&time=${raw.to.unix()}`, url: `proxied/api/v1/query?query=metric&time=${raw.to.unix()}`,
requestId: undefined, requestId: undefined,
...@@ -237,8 +241,8 @@ describe('PrometheusMetricFindQuery', () => { ...@@ -237,8 +241,8 @@ describe('PrometheusMetricFindQuery', () => {
expect(results[0].text).toBe('up{instance="127.0.0.1:1234",job="job1"}'); expect(results[0].text).toBe('up{instance="127.0.0.1:1234",job="job1"}');
expect(results[1].text).toBe('up{instance="127.0.0.1:5678",job="job1"}'); expect(results[1].text).toBe('up{instance="127.0.0.1:5678",job="job1"}');
expect(results[2].text).toBe('up{instance="127.0.0.1:9102",job="job1"}'); expect(results[2].text).toBe('up{instance="127.0.0.1:9102",job="job1"}');
expect(datasourceRequestMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
expect(datasourceRequestMock).toHaveBeenCalledWith({ expect(fetchMock).toHaveBeenCalledWith({
method: 'GET', method: 'GET',
url: `proxied/api/v1/series?match${encodeURIComponent('[]')}=${encodeURIComponent( url: `proxied/api/v1/series?match${encodeURIComponent('[]')}=${encodeURIComponent(
'up{job="job1"}' 'up{job="job1"}'
......
import _ from 'lodash'; import _ from 'lodash';
import { TimeRange, MetricFindValue } from '@grafana/data'; import { map } from 'rxjs/operators';
import { PrometheusDatasource, PromDataQueryResponse } from './datasource'; import { MetricFindValue, TimeRange } from '@grafana/data';
import { PromDataQueryResponse, PrometheusDatasource } from './datasource';
import { PromQueryRequest } from './types'; import { PromQueryRequest } from './types';
import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
...@@ -39,7 +40,7 @@ export default class PrometheusMetricFindQuery { ...@@ -39,7 +40,7 @@ export default class PrometheusMetricFindQuery {
const queryResultQuery = this.query.match(queryResultRegex); const queryResultQuery = this.query.match(queryResultRegex);
if (queryResultQuery) { if (queryResultQuery) {
return this.queryResultQuery(queryResultQuery[1]); return this.queryResultQuery(queryResultQuery[1]).toPromise();
} }
// if query contains full metric name, return metric name and label list // if query contains full metric name, return metric name and label list
...@@ -116,24 +117,26 @@ export default class PrometheusMetricFindQuery { ...@@ -116,24 +117,26 @@ export default class PrometheusMetricFindQuery {
queryResultQuery(query: string) { queryResultQuery(query: string) {
const end = this.datasource.getPrometheusTime(this.range.to, true); const end = this.datasource.getPrometheusTime(this.range.to, true);
const instantQuery: PromQueryRequest = { expr: query } as PromQueryRequest; const instantQuery: PromQueryRequest = { expr: query } as PromQueryRequest;
return this.datasource.performInstantQuery(instantQuery, end).then((result: PromDataQueryResponse) => { return this.datasource.performInstantQuery(instantQuery, end).pipe(
return _.map(result.data.data.result, metricData => { map((result: PromDataQueryResponse) => {
let text = metricData.metric.__name__ || ''; return _.map(result.data.data.result, metricData => {
delete metricData.metric.__name__; let text = metricData.metric.__name__ || '';
text += delete metricData.metric.__name__;
'{' + text +=
_.map(metricData.metric, (v, k) => { '{' +
return k + '="' + v + '"'; _.map(metricData.metric, (v, k) => {
}).join(',') + return k + '="' + v + '"';
'}'; }).join(',') +
text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000; '}';
text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000;
return { return {
text: text, text: text,
expandable: true, expandable: true,
}; };
}); });
}); })
);
} }
metricNameAndLabelsQuery(query: string): Promise<MetricFindValue[]> { metricNameAndLabelsQuery(query: string): Promise<MetricFindValue[]> {
......
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