mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Fix: fixes issue with headers property with different casing (#22778)
Fixes #22756
This commit is contained in:
parent
7b5b8ad3d3
commit
b30f4c7bb0
@ -589,12 +589,10 @@ export const parseUrlFromOptions = (options: BackendSrvRequest): string => {
|
||||
|
||||
export const parseInitFromOptions = (options: BackendSrvRequest): RequestInit => {
|
||||
const method = options.method;
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
...options.headers,
|
||||
};
|
||||
const body = parseBody({ ...options, headers });
|
||||
const headers = parseHeaders(options);
|
||||
const isAppJson = isContentTypeApplicationJson(headers);
|
||||
const body = parseBody(options, isAppJson);
|
||||
|
||||
return {
|
||||
method,
|
||||
headers,
|
||||
@ -602,14 +600,42 @@ export const parseInitFromOptions = (options: BackendSrvRequest): RequestInit =>
|
||||
};
|
||||
};
|
||||
|
||||
const parseBody = (options: BackendSrvRequest) => {
|
||||
export const parseHeaders = (options: BackendSrvRequest) => {
|
||||
const headers = new Headers({
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
});
|
||||
|
||||
if (options && options.headers) {
|
||||
Object.keys(options.headers).forEach(key => {
|
||||
headers.set(key, options.headers[key]);
|
||||
});
|
||||
}
|
||||
|
||||
return headers;
|
||||
};
|
||||
|
||||
export const isContentTypeApplicationJson = (headers: Headers) => {
|
||||
if (!headers) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const contentType = headers.get('content-type');
|
||||
if (contentType && contentType.toLowerCase() === 'application/json') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const parseBody = (options: BackendSrvRequest, isAppJson: boolean) => {
|
||||
if (!options) {
|
||||
return options;
|
||||
}
|
||||
|
||||
if (!options.data || typeof options.data === 'string') {
|
||||
return options.data;
|
||||
}
|
||||
|
||||
if (options.headers['Content-Type'] === 'application/json') {
|
||||
return JSON.stringify(options.data);
|
||||
}
|
||||
|
||||
return new URLSearchParams(options.data);
|
||||
return isAppJson ? JSON.stringify(options.data) : new URLSearchParams(options.data);
|
||||
};
|
||||
|
@ -1,10 +1,20 @@
|
||||
import { BackendSrv, getBackendSrv, parseInitFromOptions, parseUrlFromOptions } from '../services/backend_srv';
|
||||
import 'whatwg-fetch'; // fetch polyfill needed for PhantomJs rendering
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
|
||||
import {
|
||||
BackendSrv,
|
||||
getBackendSrv,
|
||||
isContentTypeApplicationJson,
|
||||
parseBody,
|
||||
parseHeaders,
|
||||
parseInitFromOptions,
|
||||
parseUrlFromOptions,
|
||||
} from '../services/backend_srv';
|
||||
import { Emitter } from '../utils/emitter';
|
||||
import { ContextSrv, User } from '../services/context_srv';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { CoreEvents } from '../../types';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
const getTestContext = (overides?: object) => {
|
||||
const defaults = {
|
||||
@ -174,7 +184,9 @@ describe('backendSrv', () => {
|
||||
statusText: 'Ok',
|
||||
text: () => Promise.resolve(JSON.stringify(slowData)),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
},
|
||||
redirected: false,
|
||||
type: 'basic',
|
||||
@ -189,7 +201,9 @@ describe('backendSrv', () => {
|
||||
statusText: 'Ok',
|
||||
text: () => Promise.resolve(JSON.stringify(fastData)),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
},
|
||||
redirected: false,
|
||||
type: 'basic',
|
||||
@ -358,8 +372,10 @@ describe('backendSrv', () => {
|
||||
method: 'GET',
|
||||
body: undefined,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json, text/plain, */*',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -389,8 +405,10 @@ describe('backendSrv', () => {
|
||||
method: 'GET',
|
||||
body: undefined as any,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json, text/plain, */*',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -415,7 +433,9 @@ describe('backendSrv', () => {
|
||||
statusText: 'Ok',
|
||||
text: () => Promise.resolve(JSON.stringify(slowData)),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
},
|
||||
redirected: false,
|
||||
type: 'basic',
|
||||
@ -430,7 +450,9 @@ describe('backendSrv', () => {
|
||||
statusText: 'Ok',
|
||||
text: () => Promise.resolve(JSON.stringify(fastData)),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
},
|
||||
redirected: false,
|
||||
type: 'basic',
|
||||
@ -448,7 +470,9 @@ describe('backendSrv', () => {
|
||||
expect(fastResponse).toEqual({
|
||||
data: { message: 'Fast Request' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
},
|
||||
ok: true,
|
||||
redirected: false,
|
||||
@ -461,8 +485,10 @@ describe('backendSrv', () => {
|
||||
method: 'GET',
|
||||
body: undefined,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json, text/plain, */*',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -477,8 +503,10 @@ describe('backendSrv', () => {
|
||||
method: 'GET',
|
||||
body: undefined,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
map: {
|
||||
'content-type': 'application/json',
|
||||
accept: 'application/json, text/plain, */*',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -634,22 +662,65 @@ describe('parseUrlFromOptions', () => {
|
||||
|
||||
describe('parseInitFromOptions', () => {
|
||||
it.each`
|
||||
method | headers | data | expected
|
||||
${undefined} | ${undefined} | ${undefined} | ${{ method: undefined, headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/plain, */*' }, body: undefined }}
|
||||
${'GET'} | ${undefined} | ${undefined} | ${{ method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/plain, */*' }, body: undefined }}
|
||||
${'GET'} | ${undefined} | ${null} | ${{ method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/plain, */*' }, body: null }}
|
||||
${'GET'} | ${{ Auth: 'Some Auth' }} | ${undefined} | ${{ method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: undefined }}
|
||||
${'GET'} | ${{ Auth: 'Some Auth' }} | ${{ data: { test: 'Some data' } }} | ${{ method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: '{"data":{"test":"Some data"}}' }}
|
||||
${'GET'} | ${{ Auth: 'Some Auth' }} | ${'some data'} | ${{ method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: 'some data' }}
|
||||
${'GET'} | ${{ Auth: 'Some Auth' }} | ${'{"data":{"test":"Some data"}}'} | ${{ method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: '{"data":{"test":"Some data"}}' }}
|
||||
${'POST'} | ${{ Auth: 'Some Auth', 'Content-Type': 'application/x-www-form-urlencoded' }} | ${undefined} | ${{ method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: undefined }}
|
||||
${'POST'} | ${{ Auth: 'Some Auth', 'Content-Type': 'application/x-www-form-urlencoded' }} | ${{ data: 'Some data' }} | ${{ method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: new URLSearchParams({ data: 'Some data' }) }}
|
||||
${'POST'} | ${{ Auth: 'Some Auth', 'Content-Type': 'application/x-www-form-urlencoded' }} | ${'some data'} | ${{ method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: 'some data' }}
|
||||
${'POST'} | ${{ Auth: 'Some Auth', 'Content-Type': 'application/x-www-form-urlencoded' }} | ${'{"data":{"test":"Some data"}}'} | ${{ method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json, text/plain, */*', Auth: 'Some Auth' }, body: '{"data":{"test":"Some data"}}' }}
|
||||
method | expected
|
||||
${undefined} | ${{ method: undefined, headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
|
||||
${'GET'} | ${{ method: 'GET', headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
|
||||
${'POST'} | ${{ method: 'POST', headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
|
||||
${'monkey'} | ${{ method: 'monkey', headers: { map: { 'content-type': 'application/json', accept: 'application/json, text/plain, */*' } }, body: '{"id":"0"}' }}
|
||||
`(
|
||||
"when called with method: '$method', headers: '$headers' and data: '$data' then result should be '$expected'",
|
||||
({ method, headers, data, expected }) => {
|
||||
expect(parseInitFromOptions({ method, headers, data, url: '' })).toEqual(expected);
|
||||
({ method, expected }) => {
|
||||
expect(parseInitFromOptions({ method, data: { id: '0' }, url: '' })).toEqual(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('parseHeaders', () => {
|
||||
it.each`
|
||||
options | expected
|
||||
${undefined} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'application/json' } }}
|
||||
${{ propKey: 'some prop value' }} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'application/json' } }}
|
||||
${{ headers: { 'content-type': 'application/json' } }} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'application/json' } }}
|
||||
${{ headers: { 'cOnTent-tYpe': 'application/json' } }} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'application/json' } }}
|
||||
${{ headers: { 'content-type': 'AppLiCatIon/JsOn' } }} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'AppLiCatIon/JsOn' } }}
|
||||
${{ headers: { 'cOnTent-tYpe': 'AppLiCatIon/JsOn' } }} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'AppLiCatIon/JsOn' } }}
|
||||
${{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'application/x-www-form-urlencoded' } }}
|
||||
${{ headers: { Accept: 'text/plain' } }} | ${{ map: { accept: 'text/plain', 'content-type': 'application/json' } }}
|
||||
${{ headers: { Auth: 'Basic asdasdasd' } }} | ${{ map: { accept: 'application/json, text/plain, */*', 'content-type': 'application/json', auth: 'Basic asdasdasd' } }}
|
||||
`("when called with options: '$options' then the result should be '$expected'", ({ options, expected }) => {
|
||||
expect(parseHeaders(options)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isContentTypeApplicationJson', () => {
|
||||
it.each`
|
||||
headers | expected
|
||||
${undefined} | ${false}
|
||||
${new Headers({ 'cOnTent-tYpe': 'application/json' })} | ${true}
|
||||
${new Headers({ 'content-type': 'AppLiCatIon/JsOn' })} | ${true}
|
||||
${new Headers({ 'cOnTent-tYpe': 'AppLiCatIon/JsOn' })} | ${true}
|
||||
${new Headers({ 'content-type': 'application/x-www-form-urlencoded' })} | ${false}
|
||||
${new Headers({ auth: 'Basic akdjasdkjalksdjasd' })} | ${false}
|
||||
`("when called with headers: 'headers' then the result should be '$expected'", ({ headers, expected }) => {
|
||||
expect(isContentTypeApplicationJson(headers)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseBody', () => {
|
||||
it.each`
|
||||
options | isAppJson | expected
|
||||
${undefined} | ${false} | ${undefined}
|
||||
${undefined} | ${true} | ${undefined}
|
||||
${{ data: undefined }} | ${false} | ${undefined}
|
||||
${{ data: undefined }} | ${true} | ${undefined}
|
||||
${{ data: 'some data' }} | ${false} | ${'some data'}
|
||||
${{ data: 'some data' }} | ${true} | ${'some data'}
|
||||
${{ data: { id: '0' } }} | ${false} | ${new URLSearchParams({ id: '0' })}
|
||||
${{ data: { id: '0' } }} | ${true} | ${'{"id":"0"}'}
|
||||
`(
|
||||
"when called with options: '$options' and isAppJson: '$isAppJson' then the result should be '$expected'",
|
||||
({ options, isAppJson, expected }) => {
|
||||
expect(parseBody(options, isAppJson)).toEqual(expected);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user