BackendSrv: support binary responseType like $http did (#29004)

This commit is contained in:
Ryan McKinley
2020-11-18 09:35:28 -08:00
committed by GitHub
parent 1895626080
commit d85d547ab3
4 changed files with 84 additions and 9 deletions

View File

@@ -61,6 +61,15 @@ export type BackendSrvRequest = {
*/ */
params?: Record<string, any>; params?: Record<string, any>;
/**
* Define how the response object should be parsed. See:
*
* https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data
*
* By default values are json parsed from text
*/
responseType?: 'json' | 'text' | 'arraybuffer' | 'blob';
/** /**
* The credentials read-only property of the Request interface indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests. * The credentials read-only property of the Request interface indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests.
*/ */

View File

@@ -11,7 +11,7 @@ import { DashboardSearchHit } from 'app/features/search/types';
import { FolderDTO } from 'app/types'; import { FolderDTO } from 'app/types';
import { coreModule } from 'app/core/core_module'; import { coreModule } from 'app/core/core_module';
import { ContextSrv, contextSrv } from './context_srv'; import { ContextSrv, contextSrv } from './context_srv';
import { parseInitFromOptions, parseUrlFromOptions } from '../utils/fetch'; import { parseInitFromOptions, parseResponseBody, parseUrlFromOptions } from '../utils/fetch';
import { isDataQuery, isLocalUrl } from '../utils/query'; import { isDataQuery, isLocalUrl } from '../utils/query';
import { FetchQueue } from './FetchQueue'; import { FetchQueue } from './FetchQueue';
import { ResponseQueue } from './ResponseQueue'; import { ResponseQueue } from './ResponseQueue';
@@ -175,15 +175,8 @@ export class BackendSrv implements BackendService {
return this.dependencies.fromFetch(url, init).pipe( return this.dependencies.fromFetch(url, init).pipe(
mergeMap(async response => { mergeMap(async response => {
const { status, statusText, ok, headers, url, type, redirected } = response; const { status, statusText, ok, headers, url, type, redirected } = response;
const textData = await response.text(); // this could be just a string, prometheus requests for instance
let data: T;
try {
data = JSON.parse(textData); // majority of the requests this will be something that can be parsed
} catch {
data = textData as any;
}
const data = await parseResponseBody<T>(response, options.responseType);
const fetchResponse: FetchResponse<T> = { const fetchResponse: FetchResponse<T> = {
status, status,
statusText, statusText,

View File

@@ -5,6 +5,7 @@ import {
parseCredentials, parseCredentials,
parseHeaders, parseHeaders,
parseInitFromOptions, parseInitFromOptions,
parseResponseBody,
parseUrlFromOptions, parseUrlFromOptions,
} from './fetch'; } from './fetch';
@@ -127,3 +128,48 @@ describe('parseCredentials', () => {
} }
); );
}); });
describe('parseResponseBody', () => {
const rsp = ({} as unknown) as Response;
it('parses json', async () => {
const value = { hello: 'world' };
const body = await parseResponseBody(
{
...rsp,
json: jest.fn().mockImplementationOnce(() => value),
},
'json'
);
expect(body).toEqual(value);
});
it('parses text', async () => {
const value = 'RAW TEXT';
const body = await parseResponseBody(
{
...rsp,
text: jest.fn().mockImplementationOnce(() => value),
},
'text'
);
expect(body).toEqual(value);
});
it('undefined text', async () => {
const value = 'RAW TEXT';
const body = await parseResponseBody({
...rsp,
text: jest.fn().mockImplementationOnce(() => value),
});
expect(body).toEqual(value);
});
it('undefined as parsed json', async () => {
const value = { hello: 'world' };
const body = await parseResponseBody({
...rsp,
text: jest.fn().mockImplementationOnce(() => JSON.stringify(value)),
});
expect(body).toEqual(value);
});
});

View File

@@ -91,6 +91,33 @@ export const parseBody = (options: BackendSrvRequest, isAppJson: boolean) => {
return isAppJson ? JSON.stringify(options.data) : new URLSearchParams(options.data); return isAppJson ? JSON.stringify(options.data) : new URLSearchParams(options.data);
}; };
export async function parseResponseBody<T>(
response: Response,
responseType?: 'json' | 'text' | 'arraybuffer' | 'blob'
): Promise<T> {
if (responseType) {
switch (responseType) {
case 'arraybuffer':
return response.arrayBuffer() as any;
case 'blob':
return response.blob() as any;
case 'json':
return response.json();
case 'text':
return response.text() as any;
}
}
const textData = await response.text(); // this could be just a string, prometheus requests for instance
try {
return JSON.parse(textData); // majority of the requests this will be something that can be parsed
} catch {}
return textData as any;
}
export function serializeParams(data: Record<string, any>): string { export function serializeParams(data: Record<string, any>): string {
return Object.keys(data) return Object.keys(data)
.map(key => { .map(key => {