fix(#2767): Fix serilization issues of bigint in json body (#2773)

Fix serilization issues of bigint in json body
---------

Co-authored-by: lohit <lohit.jiddimani@gmail.com>
This commit is contained in:
Anoop M D 2024-08-27 14:12:56 +05:30 committed by GitHub
parent b4fd350334
commit 9e5148f032
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 158 additions and 30 deletions

View File

@ -67,6 +67,7 @@ if (!SERVER_RENDERED) {
'bru.setVar(key,value)', 'bru.setVar(key,value)',
'bru.deleteVar(key)', 'bru.deleteVar(key)',
'bru.setNextRequest(requestName)', 'bru.setNextRequest(requestName)',
'req.disableParsingResponseJson()'
'bru.getRequestVar(key)', 'bru.getRequestVar(key)',
'bru.sleep(ms)' 'bru.sleep(ms)'
]; ];

View File

@ -17,8 +17,6 @@ const Timeline = ({ request, response }) => {
}); });
}); });
let requestData = safeStringifyJSON(request.data);
return ( return (
<StyledWrapper className="pb-4 w-full"> <StyledWrapper className="pb-4 w-full">
<div> <div>
@ -33,9 +31,10 @@ const Timeline = ({ request, response }) => {
); );
})} })}
{requestData ? ( {request.data ? (
<pre className="line request"> <pre className="line request">
<span className="arrow">{'>'}</span> data {requestData} <span className="arrow">{'>'}</span> data{' '}
<pre className="text-sm flex flex-wrap whitespace-break-spaces">{request.data}</pre>
</pre> </pre>
) : null} ) : null}
</div> </div>

View File

@ -272,7 +272,7 @@ const configureRequest = async (
return axiosInstance; return axiosInstance;
}; };
const parseDataFromResponse = (response) => { const parseDataFromResponse = (response, disableParsingResponseJson = false) => {
// Parse the charset from content type: https://stackoverflow.com/a/33192813 // Parse the charset from content type: https://stackoverflow.com/a/33192813
const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(response.headers['content-type'] || ''); const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(response.headers['content-type'] || '');
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#using_exec_with_regexp_literals // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#using_exec_with_regexp_literals
@ -290,7 +290,9 @@ const parseDataFromResponse = (response) => {
// Filter out ZWNBSP character // Filter out ZWNBSP character
// https://gist.github.com/antic183/619f42b559b78028d1fe9e7ae8a1352d // https://gist.github.com/antic183/619f42b559b78028d1fe9e7ae8a1352d
data = data.replace(/^\uFEFF/, ''); data = data.replace(/^\uFEFF/, '');
if(!disableParsingResponseJson) {
data = JSON.parse(data); data = JSON.parse(data);
}
} catch {} } catch {}
return { data, dataBuffer }; return { data, dataBuffer };
@ -540,7 +542,7 @@ const registerNetworkIpc = (mainWindow) => {
// Continue with the rest of the request lifecycle - post response vars, script, assertions, tests // Continue with the rest of the request lifecycle - post response vars, script, assertions, tests
const { data, dataBuffer } = parseDataFromResponse(response); const { data, dataBuffer } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson);
response.data = data; response.data = data;
response.responseTime = responseTime; response.responseTime = responseTime;
@ -701,7 +703,7 @@ const registerNetworkIpc = (mainWindow) => {
} }
} }
const { data } = parseDataFromResponse(response); const { data } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson);
response.data = data; response.data = data;
await runPostResponse( await runPostResponse(
@ -969,7 +971,7 @@ const registerNetworkIpc = (mainWindow) => {
response = await axiosInstance(request); response = await axiosInstance(request);
timeEnd = Date.now(); timeEnd = Date.now();
const { data, dataBuffer } = parseDataFromResponse(response); const { data, dataBuffer } = parseDataFromResponse(response, request.__brunoDisableParsingResponseJson);
response.data = data; response.data = data;
response.responseTime = response.headers.get('request-duration'); response.responseTime = response.headers.get('request-duration');

View File

@ -59,14 +59,6 @@ const interpolateVars = (request, envVars = {}, runtimeVariables = {}, processEn
const contentType = getContentType(request.headers); const contentType = getContentType(request.headers);
if (contentType.includes('json')) { if (contentType.includes('json')) {
if (typeof request.data === 'object') {
try {
let parsed = JSON.stringify(request.data);
parsed = _interpolate(parsed);
request.data = JSON.parse(parsed);
} catch (err) {}
}
if (typeof request.data === 'string') { if (typeof request.data === 'string') {
if (request.data.length) { if (request.data.length) {
request.data = _interpolate(request.data); request.data = _interpolate(request.data);

View File

@ -1,7 +1,6 @@
const os = require('os'); const os = require('os');
const { get, each, filter, extend, compact } = require('lodash'); const { get, each, filter, extend, compact } = require('lodash');
const decomment = require('decomment'); const decomment = require('decomment');
var JSONbig = require('json-bigint');
const FormData = require('form-data'); const FormData = require('form-data');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
@ -342,16 +341,10 @@ const prepareRequest = (item, collection) => {
if (!contentTypeDefined) { if (!contentTypeDefined) {
axiosRequest.headers['content-type'] = 'application/json'; axiosRequest.headers['content-type'] = 'application/json';
} }
let jsonBody;
try { try {
jsonBody = decomment(request?.body?.json); axiosRequest.data = decomment(request?.body?.json);
} catch (error) { } catch (error) {
jsonBody = request?.body?.json; axiosRequest.data = request?.body?.json;
}
try {
axiosRequest.data = JSONbig.parse(jsonBody);
} catch (error) {
axiosRequest.data = jsonBody;
} }
} }

View File

@ -6,7 +6,7 @@ describe('prepare-request: prepareRequest', () => {
describe('Decomments request body', () => { describe('Decomments request body', () => {
it('If request body is valid JSON', async () => { it('If request body is valid JSON', async () => {
const body = { mode: 'json', json: '{\n"test": "{{someVar}}" // comment\n}' }; const body = { mode: 'json', json: '{\n"test": "{{someVar}}" // comment\n}' };
const expected = { test: '{{someVar}}' }; const expected = '{\n"test": "{{someVar}}" \n}';
const result = prepareRequest({ request: { body } }, {}); const result = prepareRequest({ request: { body } }, {});
expect(result.data).toEqual(expected); expect(result.data).toEqual(expected);
}); });

View File

@ -1,11 +1,34 @@
class BrunoRequest { class BrunoRequest {
/**
* The following properties are available as shorthand:
* - req.url
* - req.method
* - req.headers
* - req.timeout
* - req.body
*
* Above shorthands are useful for accessing the request properties directly in the scripts
* It must be noted that the user cannot set these properties directly.
* They should use the respective setter methods to set these properties.
*/
constructor(req) { constructor(req) {
this.req = req; this.req = req;
this.url = req.url; this.url = req.url;
this.method = req.method; this.method = req.method;
this.headers = req.headers; this.headers = req.headers;
this.body = req.data;
this.timeout = req.timeout; this.timeout = req.timeout;
/**
* We automatically parse the JSON body if the content type is JSON
* This is to make it easier for the user to access the body directly
*
* It must be noted that the request data is always a string and is what gets sent over the network
* If the user wants to access the raw data, they can use getBody({raw: true}) method
*/
const isJson = this.hasJSONContentType(this.req.headers);
if (isJson) {
this.body = this.__safeParseJSON(req.data);
}
} }
getUrl() { getUrl() {
@ -13,6 +36,7 @@ class BrunoRequest {
} }
setUrl(url) { setUrl(url) {
this.url = url;
this.req.url = url; this.req.url = url;
} }
@ -37,6 +61,7 @@ class BrunoRequest {
} }
setMethod(method) { setMethod(method) {
this.method = method;
this.req.method = method; this.req.method = method;
} }
@ -45,6 +70,7 @@ class BrunoRequest {
} }
setHeaders(headers) { setHeaders(headers) {
this.headers = headers;
this.req.headers = headers; this.req.headers = headers;
} }
@ -53,15 +79,60 @@ class BrunoRequest {
} }
setHeader(name, value) { setHeader(name, value) {
this.headers[name] = value;
this.req.headers[name] = value; this.req.headers[name] = value;
} }
getBody() { hasJSONContentType(headers) {
const contentType = headers?.['Content-Type'] || headers?.['content-type'] || '';
return contentType.includes('json');
}
/**
* Get the body of the request
*
* We automatically parse and return the JSON body if the content type is JSON
* If the user wants the raw body, they can pass the raw option as true
*/
getBody(options = {}) {
if (options.raw) {
return this.req.data; return this.req.data;
} }
setBody(data) { const isJson = this.hasJSONContentType(this.req.headers);
if (isJson) {
return this.__safeParseJSON(this.req.data);
}
return this.req.data;
}
/**
* If the content type is JSON and if the data is an object
* - We set the body property as the object itself
* - We set the request data as the stringified JSON as it is what gets sent over the network
* Otherwise
* - We set the request data as the data itself
* - We set the body property as the data itself
*
* If the user wants to override this behavior, they can pass the raw option as true
*/
setBody(data, options = {}) {
if (options.raw) {
this.req.data = data; this.req.data = data;
this.body = data;
return;
}
const isJson = this.hasJSONContentType(this.req.headers);
if (isJson && this.__isObject(data)) {
this.body = data;
this.req.data = this.__safeStringifyJSON(data);
return;
}
this.req.data = data;
this.body = data;
} }
setMaxRedirects(maxRedirects) { setMaxRedirects(maxRedirects) {
@ -73,8 +144,34 @@ class BrunoRequest {
} }
setTimeout(timeout) { setTimeout(timeout) {
this.timeout = timeout;
this.req.timeout = timeout; this.req.timeout = timeout;
} }
__safeParseJSON(str) {
try {
return JSON.parse(str);
} catch (e) {
return str;
}
}
__safeStringifyJSON(obj) {
try {
return JSON.stringify(obj);
} catch (e) {
return obj;
}
}
__isObject(obj) {
return obj !== null && typeof obj === 'object';
}
disableParsingResponseJson() {
this.req.__brunoDisableParsingResponseJson = true;
}
} }
module.exports = BrunoRequest; module.exports = BrunoRequest;

View File

@ -0,0 +1,44 @@
meta {
name: echo bigint
type: http
seq: 6
}
post {
url: {{host}}/api/echo/json
body: json
auth: none
}
headers {
foo: bar
}
auth:basic {
username: asd
password: j
}
auth:bearer {
token:
}
body:json {
{
"hello": 990531470713421825
}
}
body:text {
{
"hello": 990531470713421825
}
}
assert {
res.status: eq 200
}
script:pre-request {
bru.setVar("foo", "foo-world-2");
}