diff --git a/public/app/plugins/datasource/mssql/datasource.ts b/public/app/plugins/datasource/mssql/datasource.ts index 79fab637042..c1084fbeef9 100644 --- a/public/app/plugins/datasource/mssql/datasource.ts +++ b/public/app/plugins/datasource/mssql/datasource.ts @@ -1,10 +1,12 @@ import _ from 'lodash'; -import ResponseParser from './response_parser'; +import { Observable, of } from 'rxjs'; +import { catchError, map, mapTo } from 'rxjs/operators'; import { getBackendSrv } from '@grafana/runtime'; import { ScopedVars } from '@grafana/data'; + +import ResponseParser, { MssqlResponse } from './response_parser'; import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv'; import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv'; -//Types import { MssqlQueryForInterpolation } from './types'; export class MssqlDatasource { @@ -66,7 +68,7 @@ export class MssqlDatasource { return expandedQueries; } - query(options: any) { + query(options: any): Observable { const queries = _.filter(options.targets, item => { return item.hide !== true; }).map(item => { @@ -81,11 +83,11 @@ export class MssqlDatasource { }); if (queries.length === 0) { - return Promise.resolve({ data: [] }); + return of({ data: [] }); } return getBackendSrv() - .datasourceRequest({ + .fetch({ url: '/api/tsdb/query', method: 'POST', data: { @@ -94,7 +96,7 @@ export class MssqlDatasource { queries: queries, }, }) - .then(this.responseParser.processQueryResult); + .pipe(map(this.responseParser.processQueryResult)); } annotationQuery(options: any) { @@ -110,7 +112,7 @@ export class MssqlDatasource { }; return getBackendSrv() - .datasourceRequest({ + .fetch({ url: '/api/tsdb/query', method: 'POST', data: { @@ -119,7 +121,8 @@ export class MssqlDatasource { queries: [query], }, }) - .then((data: any) => this.responseParser.transformAnnotationResponse(options, data)); + .pipe(map((data: any) => this.responseParser.transformAnnotationResponse(options, data))) + .toPromise(); } metricFindQuery(query: string, optionalOptions: { variable: { name: string } }) { @@ -143,17 +146,18 @@ export class MssqlDatasource { }; return getBackendSrv() - .datasourceRequest({ + .fetch({ url: '/api/tsdb/query', method: 'POST', data: data, }) - .then((data: any) => this.responseParser.parseMetricFindQueryResult(refId, data)); + .pipe(map((data: any) => this.responseParser.parseMetricFindQueryResult(refId, data))) + .toPromise(); } testDatasource() { return getBackendSrv() - .datasourceRequest({ + .fetch({ url: '/api/tsdb/query', method: 'POST', data: { @@ -171,17 +175,18 @@ export class MssqlDatasource { ], }, }) - .then((res: any) => { - return { status: 'success', message: 'Database Connection OK' }; - }) - .catch((err: any) => { - console.error(err); - if (err.data && err.data.message) { - return { status: 'error', message: err.data.message }; - } else { - return { status: 'error', message: err.status }; - } - }); + .pipe( + mapTo({ status: 'success', message: 'Database Connection OK' }), + catchError(err => { + console.error(err); + if (err.data && err.data.message) { + return of({ status: 'error', message: err.data.message }); + } + + return of({ status: 'error', message: err.status }); + }) + ) + .toPromise(); } targetContainsTemplate(target: any) { diff --git a/public/app/plugins/datasource/mssql/response_parser.ts b/public/app/plugins/datasource/mssql/response_parser.ts index ca456ebc9bf..f60084fbdb7 100644 --- a/public/app/plugins/datasource/mssql/response_parser.ts +++ b/public/app/plugins/datasource/mssql/response_parser.ts @@ -1,7 +1,25 @@ import _ from 'lodash'; +import { MetricFindValue } from '@grafana/data'; + +interface TableResponse extends Record { + type: string; + refId: string; + meta: any; +} + +interface SeriesResponse extends Record { + target: string; + refId: string; + meta: any; + datapoints: [any[]]; +} + +export interface MssqlResponse { + data: Array; +} export default class ResponseParser { - processQueryResult(res: any) { + processQueryResult(res: any): MssqlResponse { const data: any[] = []; if (!res.data.results) { @@ -35,7 +53,7 @@ export default class ResponseParser { return { data: data }; } - parseMetricFindQueryResult(refId: string, results: any) { + parseMetricFindQueryResult(refId: string, results: any): MetricFindValue[] { if (!results || results.data.length === 0 || results.data.results[refId].meta.rowCount === 0) { return []; } @@ -52,7 +70,7 @@ export default class ResponseParser { return this.transformToSimpleList(rows); } - transformToKeyValueList(rows: any, textColIndex: number, valueColIndex: number) { + transformToKeyValueList(rows: any, textColIndex: number, valueColIndex: number): MetricFindValue[] { const res = []; for (let i = 0; i < rows.length; i++) { @@ -64,7 +82,7 @@ export default class ResponseParser { return res; } - transformToSimpleList(rows: any) { + transformToSimpleList(rows: any): MetricFindValue[] { const res = []; for (let i = 0; i < rows.length; i++) { diff --git a/public/app/plugins/datasource/mssql/specs/datasource.test.ts b/public/app/plugins/datasource/mssql/specs/datasource.test.ts index 3fc5e853c67..4fee468c42e 100644 --- a/public/app/plugins/datasource/mssql/specs/datasource.test.ts +++ b/public/app/plugins/datasource/mssql/specs/datasource.test.ts @@ -1,10 +1,12 @@ +import { of } from 'rxjs'; +import { dateTime } from '@grafana/data'; + import { MssqlDatasource } from '../datasource'; import { TimeSrvStub } from 'test/specs/helpers'; - -import { dateTime } from '@grafana/data'; import { TemplateSrv } from 'app/features/templating/template_srv'; import { backendSrv } from 'app/core/services/backend_srv'; -import { initialCustomVariableModelState } from '../../../../features/variables/custom/reducer'; // will use the version in __mocks__ +import { initialCustomVariableModelState } from '../../../../features/variables/custom/reducer'; +import { FetchResponse } from '@grafana/runtime'; // will use the version in __mocks__ jest.mock('@grafana/runtime', () => ({ ...((jest.requireActual('@grafana/runtime') as unknown) as object), @@ -13,7 +15,7 @@ jest.mock('@grafana/runtime', () => ({ describe('MSSQLDatasource', () => { const templateSrv: TemplateSrv = new TemplateSrv(); - const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest'); + const fetchMock = jest.spyOn(backendSrv, 'fetch'); const ctx: any = { timeSrv: new TimeSrvStub(), @@ -61,7 +63,7 @@ describe('MSSQLDatasource', () => { }; beforeEach(() => { - datasourceRequestMock.mockImplementation((options: any) => Promise.resolve({ data: response, status: 200 })); + fetchMock.mockImplementation(() => of(createFetchResponse(response))); return ctx.ds.annotationQuery(options).then((data: any) => { results = data; @@ -107,7 +109,7 @@ describe('MSSQLDatasource', () => { }; beforeEach(() => { - datasourceRequestMock.mockImplementation((options: any) => Promise.resolve({ data: response, status: 200 })); + fetchMock.mockImplementation(() => of(createFetchResponse(response))); return ctx.ds.metricFindQuery(query).then((data: any) => { results = data; @@ -146,7 +148,7 @@ describe('MSSQLDatasource', () => { }; beforeEach(() => { - datasourceRequestMock.mockImplementation((options: any) => Promise.resolve({ data: response, status: 200 })); + fetchMock.mockImplementation(() => of(createFetchResponse(response))); return ctx.ds.metricFindQuery(query).then((data: any) => { results = data; @@ -187,7 +189,7 @@ describe('MSSQLDatasource', () => { }; beforeEach(() => { - datasourceRequestMock.mockImplementation((options: any) => Promise.resolve({ data: response, status: 200 })); + fetchMock.mockImplementation(() => of(createFetchResponse(response))); return ctx.ds.metricFindQuery(query).then((data: any) => { results = data; }); @@ -201,7 +203,6 @@ describe('MSSQLDatasource', () => { }); describe('When performing metricFindQuery', () => { - let results: any; const query = 'select * from atable'; const response = { results: { @@ -227,19 +228,17 @@ describe('MSSQLDatasource', () => { beforeEach(() => { ctx.timeSrv.setTime(time); - datasourceRequestMock.mockImplementation((options: any) => { - results = options.data; - return Promise.resolve({ data: response, status: 200 }); - }); + fetchMock.mockImplementation(() => of(createFetchResponse(response))); return ctx.ds.metricFindQuery(query); }); it('should pass timerange to datasourceRequest', () => { - expect(results.from).toBe(time.from.valueOf().toString()); - expect(results.to).toBe(time.to.valueOf().toString()); - expect(results.queries.length).toBe(1); - expect(results.queries[0].rawSql).toBe(query); + expect(fetchMock).toBeCalledTimes(1); + expect(fetchMock.mock.calls[0][0].data.from).toBe(time.from.valueOf().toString()); + expect(fetchMock.mock.calls[0][0].data.to).toBe(time.to.valueOf().toString()); + expect(fetchMock.mock.calls[0][0].data.queries.length).toBe(1); + expect(fetchMock.mock.calls[0][0].data.queries[0].rawSql).toBe(query); }); }); @@ -335,3 +334,17 @@ describe('MSSQLDatasource', () => { }); }); }); + +function createFetchResponse(data: T): FetchResponse { + return { + data, + status: 200, + url: 'http://localhost:3000/api/query', + config: { url: 'http://localhost:3000/api/query' }, + type: 'basic', + statusText: 'Ok', + redirected: false, + headers: ({} as unknown) as Headers, + ok: true, + }; +}