From a73e6f728d8c34fc0496c303c6f51a27f306475e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Thu, 20 Aug 2020 21:03:59 -0700 Subject: [PATCH] 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> --- .../datasource/prometheus/datasource.test.ts | 127 +++++++++--------- .../datasource/prometheus/datasource.ts | 61 +++++---- .../prometheus/language_provider.ts | 12 +- .../prometheus/metric_find_query.test.ts | 42 +++--- .../prometheus/metric_find_query.ts | 43 +++--- 5 files changed, 149 insertions(+), 136 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/datasource.test.ts b/public/app/plugins/datasource/prometheus/datasource.test.ts index a012a8894d7..939a2f1048c 100644 --- a/public/app/plugins/datasource/prometheus/datasource.test.ts +++ b/public/app/plugins/datasource/prometheus/datasource.test.ts @@ -22,13 +22,14 @@ import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { VariableHide } from '../../../features/variables/types'; import { describe } from '../../../../test/lib/common'; import { QueryOptions } from 'app/types'; +import { of, throwError } from 'rxjs'; -const datasourceRequestMock = jest.fn().mockResolvedValue(createDefaultPromResponse()); +const fetchMock = jest.fn().mockReturnValue(of(createDefaultPromResponse())); jest.mock('./metric_find_query'); jest.mock('@grafana/runtime', () => ({ getBackendSrv: () => ({ - datasourceRequest: datasourceRequestMock, + fetch: fetchMock, }), })); @@ -56,7 +57,7 @@ const replaceMock = (templateSrv.replace as any) as jest.Mock; const getTimeSrvMock = (getTimeSrv as any) as jest.Mock; beforeEach(() => { - datasourceRequestMock.mockClear(); + fetchMock.mockClear(); getAdhocFiltersMock.mockClear(); replaceMock.mockClear(); getTimeSrvMock.mockClear(); @@ -137,8 +138,8 @@ describe('PrometheusDatasource', () => { describe('Datasource metadata requests', () => { it('should perform a GET request with the default config', () => { ds.metadataRequest('/foo'); - expect(datasourceRequestMock.mock.calls.length).toBe(1); - expect(datasourceRequestMock.mock.calls[0][0].method).toBe('GET'); + expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls[0][0].method).toBe('GET'); }); it('should still perform a GET request with the DS HTTP method set to POST', () => { @@ -146,8 +147,8 @@ describe('PrometheusDatasource', () => { postSettings.jsonData.httpMethod = 'POST'; const promDs = new PrometheusDatasource(postSettings); promDs.metadataRequest('/foo'); - expect(datasourceRequestMock.mock.calls.length).toBe(1); - expect(datasourceRequestMock.mock.calls[0][0].method).toBe('GET'); + expect(fetchMock.mock.calls.length).toBe(1); + expect(fetchMock.mock.calls[0][0].method).toBe('GET'); }); }); @@ -204,8 +205,8 @@ describe('PrometheusDatasource', () => { describe('When performing performSuggestQuery', () => { it('should cache response', async () => { - datasourceRequestMock.mockImplementation(() => - Promise.resolve({ + fetchMock.mockImplementation(() => + of({ status: 'success', data: { data: ['value1', 'value2', 'value3'] }, }) @@ -215,7 +216,7 @@ describe('PrometheusDatasource', () => { expect(results).toHaveLength(3); - datasourceRequestMock.mockImplementation(jest.fn()); + fetchMock.mockImplementation(jest.fn()); results = await ds.performSuggestQuery('value', true); expect(results).toHaveLength(3); @@ -282,7 +283,7 @@ describe('PrometheusDatasource', () => { }, ]; - ds.performTimeSeriesQuery = jest.fn().mockReturnValue([responseMock]); + ds.performTimeSeriesQuery = jest.fn().mockReturnValue(of([responseMock])); ds.query(query).subscribe((result: any) => { const results = result.data; return expect(results).toMatchObject(expected); @@ -324,7 +325,7 @@ describe('PrometheusDatasource', () => { const expected = ['1', '2', '4', '+Inf']; - ds.performTimeSeriesQuery = jest.fn().mockReturnValue([responseMock]); + ds.performTimeSeriesQuery = jest.fn().mockReturnValue(of([responseMock])); ds.query(query).subscribe((result: any) => { const seriesLabels = _.map(result.data, 'target'); return expect(seriesLabels).toEqual(expected); @@ -609,14 +610,14 @@ describe('PrometheusDatasource', () => { }, }, }; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any).subscribe((data: any) => { results = data; }); }); it('should generate the correct query', () => { - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -646,7 +647,7 @@ describe('PrometheusDatasource', () => { }; it('should generate an error', () => { - datasourceRequestMock.mockImplementation(() => Promise.reject(response)); + fetchMock.mockImplementation(() => throwError(response)); ds.query(query as any).subscribe((e: any) => { results = e.message; expect(results).toBe(`"${errMessage}"`); @@ -691,7 +692,7 @@ describe('PrometheusDatasource', () => { }, }; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any).subscribe((data: any) => { results = data; @@ -754,14 +755,14 @@ describe('PrometheusDatasource', () => { }, }; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any).subscribe((data: any) => { results = data; }); }); it('should generate the correct query', () => { - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -812,7 +813,7 @@ describe('PrometheusDatasource', () => { describe('when time series query is cancelled', () => { it('should return empty results', async () => { - datasourceRequestMock.mockImplementation(() => Promise.resolve({ cancelled: true })); + fetchMock.mockImplementation(() => of({ cancelled: true })); await ds.annotationQuery(options).then((data: any) => { results = data; @@ -825,7 +826,7 @@ describe('PrometheusDatasource', () => { describe('not use useValueForTime', () => { beforeEach(async () => { options.annotation.useValueForTime = false; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); await ds.annotationQuery(options).then((data: any) => { results = data; @@ -844,7 +845,7 @@ describe('PrometheusDatasource', () => { describe('use useValueForTime', () => { beforeEach(async () => { options.annotation.useValueForTime = true; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); await ds.annotationQuery(options).then((data: any) => { results = data; @@ -858,7 +859,7 @@ describe('PrometheusDatasource', () => { describe('step parameter', () => { beforeEach(() => { - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); }); it('should use default step for short range if no interval is given', () => { @@ -870,7 +871,7 @@ describe('PrometheusDatasource', () => { }, }; ds.annotationQuery(query); - const req = datasourceRequestMock.mock.calls[0][0]; + const req = fetchMock.mock.calls[0][0]; expect(req.url).toContain('step=60'); }); @@ -887,7 +888,7 @@ describe('PrometheusDatasource', () => { }, }; ds.annotationQuery(query); - const req = datasourceRequestMock.mock.calls[0][0]; + const req = fetchMock.mock.calls[0][0]; expect(req.url).toContain('step=60'); }); @@ -905,7 +906,7 @@ describe('PrometheusDatasource', () => { }, }; ds.annotationQuery(query); - const req = datasourceRequestMock.mock.calls[0][0]; + const req = fetchMock.mock.calls[0][0]; expect(req.url).toContain('step=10'); }); @@ -923,7 +924,7 @@ describe('PrometheusDatasource', () => { }, }; ds.annotationQuery(query); - const req = datasourceRequestMock.mock.calls[0][0]; + const req = fetchMock.mock.calls[0][0]; expect(req.url).toContain('step=10'); }); @@ -936,7 +937,7 @@ describe('PrometheusDatasource', () => { }, }; ds.annotationQuery(query); - const req = datasourceRequestMock.mock.calls[0][0]; + const req = fetchMock.mock.calls[0][0]; // Range in seconds: (to - from) / 1000 // Max_datapoints: 11000 // Step: range / max_datapoints @@ -976,7 +977,7 @@ describe('PrometheusDatasource', () => { }; options.annotation.useValueForTime = false; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); return ds.annotationQuery(options); } @@ -1063,7 +1064,7 @@ describe('PrometheusDatasource', () => { }, }; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any).subscribe((data: any) => { results = data; }); @@ -1099,9 +1100,9 @@ describe('PrometheusDatasource', () => { }; const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1114,9 +1115,9 @@ describe('PrometheusDatasource', () => { interval: '100ms', }; const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=0.1'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1134,9 +1135,9 @@ describe('PrometheusDatasource', () => { interval: '10s', }; const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1151,9 +1152,9 @@ describe('PrometheusDatasource', () => { const end = 7 * 60 * 60; const start = 60 * 60; const urlExpected = 'proxied/api/v1/query_range?query=test&start=' + start + '&end=' + end + '&step=2'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1173,9 +1174,9 @@ describe('PrometheusDatasource', () => { }; // times get rounded up to interval const urlExpected = 'proxied/api/v1/query_range?query=test&start=50&end=400&step=50'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1194,9 +1195,9 @@ describe('PrometheusDatasource', () => { interval: '5s', }; const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=60&end=420&step=15'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1216,9 +1217,9 @@ describe('PrometheusDatasource', () => { }; // times get aligned to interval const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=0&end=400&step=100'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1238,9 +1239,9 @@ describe('PrometheusDatasource', () => { const end = 7 * 24 * 60 * 60; const start = 0; const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=100'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1271,9 +1272,9 @@ describe('PrometheusDatasource', () => { ); const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + adjusted.start + '&end=' + adjusted.end + '&step=' + step; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); }); @@ -1313,9 +1314,9 @@ describe('PrometheusDatasource', () => { '&start=60&end=420&step=10'; templateSrv.replace = jest.fn(str => str) as any; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); @@ -1352,10 +1353,10 @@ describe('PrometheusDatasource', () => { 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[$__interval])') + '&start=60&end=420&step=10'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); templateSrv.replace = jest.fn(str => str) as any; ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); @@ -1393,10 +1394,10 @@ describe('PrometheusDatasource', () => { 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[$__interval])') + '&start=0&end=400&step=100'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); templateSrv.replace = jest.fn(str => str) as any; ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); @@ -1441,9 +1442,9 @@ describe('PrometheusDatasource', () => { '&start=50&end=400&step=50'; templateSrv.replace = jest.fn(str => str) as any; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); @@ -1482,9 +1483,9 @@ describe('PrometheusDatasource', () => { encodeURIComponent('rate(test[$__interval])') + '&start=60&end=420&step=15'; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); @@ -1538,10 +1539,10 @@ describe('PrometheusDatasource', () => { adjusted.end + '&step=' + step; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); templateSrv.replace = jest.fn(str => str) as any; ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('GET'); expect(res.url).toBe(urlExpected); @@ -1590,9 +1591,9 @@ describe('PrometheusDatasource', () => { )}&start=0&end=3600&step=60`; templateSrv.replace = jest.fn(str => str) as any; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any); - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.url).toBe(urlExpected); // @ts-ignore @@ -1678,14 +1679,14 @@ describe('PrometheusDatasource for POST', () => { }, }, }; - datasourceRequestMock.mockImplementation(() => Promise.resolve(response)); + fetchMock.mockImplementation(() => of(response)); ds.query(query as any).subscribe((data: any) => { results = data; }); }); it('should generate the correct query', () => { - const res = datasourceRequestMock.mock.calls[0][0]; + const res = fetchMock.mock.calls[0][0]; expect(res.method).toBe('POST'); expect(res.url).toBe(urlExpected); expect(res.data).toEqual(dataExpected); diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 9938f6e6221..cc32be8557b 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -18,13 +18,13 @@ import { TimeRange, TimeSeries, } from '@grafana/data'; -import { forkJoin, from, merge, Observable, of } from 'rxjs'; -import { filter, map, tap } from 'rxjs/operators'; +import { forkJoin, merge, Observable, of, throwError } from 'rxjs'; +import { catchError, filter, map, tap } from 'rxjs/operators'; import PrometheusMetricFindQuery from './metric_find_query'; import { ResultTransformer } from './result_transformer'; 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 { getQueryHints } from './query_hints'; import { expandRecordingRules } from './language_utils'; @@ -111,7 +111,7 @@ export class PrometheusDatasource extends DataSourceApi } } - _request(url: string, data: Record | null, overrides: Partial = {}) { + _request(url: string, data: Record | null, overrides: Partial = {}) { const options: BackendSrvRequest = defaults(overrides, { url: this.url + url, method: this.httpMethod, @@ -140,12 +140,12 @@ export class PrometheusDatasource extends DataSourceApi options.headers!.Authorization = this.basicAuth; } - return getBackendSrv().datasourceRequest(options); + return getBackendSrv().fetch(options); } // Use this for tab completion features, wont publish response to other components - metadataRequest(url: string) { - return this._request(url, null, { method: 'GET', hideFromInspector: true }); + metadataRequest(url: string) { + return this._request(url, null, { method: 'GET', hideFromInspector: true }).toPromise(); // toPromise until we change getTagValues, getTagKeys to Observable } interpolateQueryExpr(value: string | string[] = [], variable: any) { @@ -272,8 +272,8 @@ export class PrometheusDatasource extends DataSourceApi const target = activeTargets[index]; let observable = query.instant - ? from(this.performInstantQuery(query, end)) - : from(this.performTimeSeriesQuery(query, query.start, query.end)); + ? this.performInstantQuery(query, end) + : this.performTimeSeriesQuery(query, query.start, query.end); return observable.pipe( // 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 const target = activeTargets[index]; let observable = query.instant - ? from(this.performInstantQuery(query, end)) - : from(this.performTimeSeriesQuery(query, query.start, query.end)); + ? this.performInstantQuery(query, end) + : this.performTimeSeriesQuery(query, query.start, query.end); return observable.pipe( filter((response: any) => (response.cancelled ? false : true)), @@ -448,13 +448,15 @@ export class PrometheusDatasource extends DataSourceApi } } - return this._request(url, data, { requestId: query.requestId, headers: query.headers }).catch((err: any) => { - if (err.cancelled) { - return err; - } + return this._request(url, data, { requestId: query.requestId, headers: query.headers }).pipe( + catchError(err => { + if (err.cancelled) { + return of(err); + } - throw this.handleErrors(err, query); - }); + return throwError(this.handleErrors(err, query)); + }) + ); } performInstantQuery(query: PromQueryRequest, time: number) { @@ -474,13 +476,15 @@ export class PrometheusDatasource extends DataSourceApi } } - return this._request(url, data, { requestId: query.requestId, headers: query.headers }).catch((err: any) => { - if (err.cancelled) { - return err; - } + return this._request(url, data, { requestId: query.requestId, headers: query.headers }).pipe( + catchError(err => { + if (err.cancelled) { + return of(err); + } - throw this.handleErrors(err, query); - }); + return throwError(this.handleErrors(err, query)); + }) + ); } handleErrors = (err: any, target: PromQuery) => { @@ -582,7 +586,11 @@ export class PrometheusDatasource extends DataSourceApi const query = this.createQuery(queryModel, queryOptions, start, end); 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 splitKeys = tagKeys.split(','); @@ -662,7 +670,7 @@ export class PrometheusDatasource extends DataSourceApi async testDatasource() { const now = new Date().getTime(); 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' ? { status: 'success', message: 'Data source is working' } : { status: 'error', message: response.error }; @@ -690,9 +698,8 @@ export class PrometheusDatasource extends DataSourceApi async loadRules() { try { 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) { this.ruleMappings = extractRuleMappingFromGroups(groups); } diff --git a/public/app/plugins/datasource/prometheus/language_provider.ts b/public/app/plugins/datasource/prometheus/language_provider.ts index 42188a29a0d..db99f63e136 100644 --- a/public/app/plugins/datasource/prometheus/language_provider.ts +++ b/public/app/plugins/datasource/prometheus/language_provider.ts @@ -2,14 +2,14 @@ import _ from 'lodash'; import LRU from 'lru-cache'; import { Value } from 'slate'; -import { dateTime, LanguageProvider, HistoryItem } from '@grafana/data'; -import { CompletionItem, TypeaheadInput, TypeaheadOutput, CompletionItemGroup } from '@grafana/ui'; +import { dateTime, HistoryItem, LanguageProvider } from '@grafana/data'; +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 { PrometheusDatasource } from './datasource'; -import { PromQuery, PromMetricsMetadata } from './types'; +import { PromMetricsMetadata, PromQuery } from './types'; const DEFAULT_KEYS = ['job', 'instance']; const EMPTY_SELECTOR = '{}'; @@ -101,9 +101,7 @@ export default class PromQlLanguageProvider extends LanguageProvider { request = async (url: string, defaultValue: any): Promise => { try { const res = await this.datasource.metadataRequest(url); - const body = await (res.data || res.json()); - - return body.data; + return res.data.data; } catch (error) { console.error(error); } diff --git a/public/app/plugins/datasource/prometheus/metric_find_query.test.ts b/public/app/plugins/datasource/prometheus/metric_find_query.test.ts index b4dd79cd5bb..55cb294ca30 100644 --- a/public/app/plugins/datasource/prometheus/metric_find_query.test.ts +++ b/public/app/plugins/datasource/prometheus/metric_find_query.test.ts @@ -1,8 +1,12 @@ +import 'whatwg-fetch'; // fetch polyfill needed backendSrv +import { of } from 'rxjs'; +import { DataSourceInstanceSettings, toUtc } from '@grafana/data'; + import { PrometheusDatasource } from './datasource'; 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 { PromOptions } from './types'; +import { FetchResponse } from '@grafana/runtime'; jest.mock('app/features/templating/template_srv', () => { return { @@ -16,7 +20,7 @@ jest.mock('@grafana/runtime', () => ({ getBackendSrv: () => backendSrv, })); -const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest'); +const fetchMock = jest.spyOn(backendSrv, 'fetch'); const instanceSettings = ({ url: 'proxied', @@ -54,7 +58,7 @@ describe('PrometheusMetricFindQuery', () => { }); 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); }; @@ -69,8 +73,8 @@ describe('PrometheusMetricFindQuery', () => { const results = await query.process(); expect(results).toHaveLength(3); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', url: 'proxied/api/v1/labels', hideFromInspector: true, @@ -88,8 +92,8 @@ describe('PrometheusMetricFindQuery', () => { const results = await query.process(); expect(results).toHaveLength(3); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', url: 'proxied/api/v1/label/resource/values', hideFromInspector: true, @@ -111,8 +115,8 @@ describe('PrometheusMetricFindQuery', () => { const results = await query.process(); expect(results).toHaveLength(3); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', url: `proxied/api/v1/series?match${encodeURIComponent( '[]' @@ -136,8 +140,8 @@ describe('PrometheusMetricFindQuery', () => { const results = await query.process(); expect(results).toHaveLength(3); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', 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', @@ -162,8 +166,8 @@ describe('PrometheusMetricFindQuery', () => { expect(results).toHaveLength(2); expect(results[0].text).toBe('value1'); expect(results[1].text).toBe('value2'); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', url: `proxied/api/v1/series?match${encodeURIComponent( '[]' @@ -183,8 +187,8 @@ describe('PrometheusMetricFindQuery', () => { const results = await query.process(); expect(results).toHaveLength(3); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', url: 'proxied/api/v1/label/__name__/values', hideFromInspector: true, @@ -211,8 +215,8 @@ describe('PrometheusMetricFindQuery', () => { expect(results).toHaveLength(1); expect(results[0].text).toBe('metric{job="testjob"} 3846 1443454528000'); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', url: `proxied/api/v1/query?query=metric&time=${raw.to.unix()}`, requestId: undefined, @@ -237,8 +241,8 @@ describe('PrometheusMetricFindQuery', () => { 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[2].text).toBe('up{instance="127.0.0.1:9102",job="job1"}'); - expect(datasourceRequestMock).toHaveBeenCalledTimes(1); - expect(datasourceRequestMock).toHaveBeenCalledWith({ + expect(fetchMock).toHaveBeenCalledTimes(1); + expect(fetchMock).toHaveBeenCalledWith({ method: 'GET', url: `proxied/api/v1/series?match${encodeURIComponent('[]')}=${encodeURIComponent( 'up{job="job1"}' diff --git a/public/app/plugins/datasource/prometheus/metric_find_query.ts b/public/app/plugins/datasource/prometheus/metric_find_query.ts index b545bb9822f..2b3d88ca003 100644 --- a/public/app/plugins/datasource/prometheus/metric_find_query.ts +++ b/public/app/plugins/datasource/prometheus/metric_find_query.ts @@ -1,6 +1,7 @@ import _ from 'lodash'; -import { TimeRange, MetricFindValue } from '@grafana/data'; -import { PrometheusDatasource, PromDataQueryResponse } from './datasource'; +import { map } from 'rxjs/operators'; +import { MetricFindValue, TimeRange } from '@grafana/data'; +import { PromDataQueryResponse, PrometheusDatasource } from './datasource'; import { PromQueryRequest } from './types'; import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv'; @@ -39,7 +40,7 @@ export default class PrometheusMetricFindQuery { const queryResultQuery = this.query.match(queryResultRegex); 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 @@ -116,24 +117,26 @@ export default class PrometheusMetricFindQuery { queryResultQuery(query: string) { const end = this.datasource.getPrometheusTime(this.range.to, true); const instantQuery: PromQueryRequest = { expr: query } as PromQueryRequest; - return this.datasource.performInstantQuery(instantQuery, end).then((result: PromDataQueryResponse) => { - return _.map(result.data.data.result, metricData => { - let text = metricData.metric.__name__ || ''; - delete metricData.metric.__name__; - text += - '{' + - _.map(metricData.metric, (v, k) => { - return k + '="' + v + '"'; - }).join(',') + - '}'; - text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000; + return this.datasource.performInstantQuery(instantQuery, end).pipe( + map((result: PromDataQueryResponse) => { + return _.map(result.data.data.result, metricData => { + let text = metricData.metric.__name__ || ''; + delete metricData.metric.__name__; + text += + '{' + + _.map(metricData.metric, (v, k) => { + return k + '="' + v + '"'; + }).join(',') + + '}'; + text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000; - return { - text: text, - expandable: true, - }; - }); - }); + return { + text: text, + expandable: true, + }; + }); + }) + ); } metricNameAndLabelsQuery(query: string): Promise {