fix: lossless json serialization

This commit is contained in:
Anoop M D 2024-08-07 13:11:58 +05:30
parent 72c3aaa5ba
commit 103e0a2444
14 changed files with 97 additions and 32 deletions

4
package-lock.json generated
View File

@ -19668,7 +19668,7 @@
},
"packages/bruno-electron": {
"name": "bruno",
"version": "v1.23.1",
"version": "v1.24.0",
"dependencies": {
"@aws-sdk/credential-providers": "3.525.0",
"@usebruno/common": "0.1.0",
@ -19698,6 +19698,7 @@
"js-yaml": "^4.1.0",
"json-bigint": "^1.0.0",
"lodash": "^4.17.21",
"lossless-json": "^4.0.1",
"mime-types": "^2.1.35",
"mustache": "^4.2.0",
"nanoid": "3.3.4",
@ -26347,6 +26348,7 @@
"js-yaml": "^4.1.0",
"json-bigint": "^1.0.0",
"lodash": "^4.17.21",
"lossless-json": "^4.0.1",
"mime-types": "^2.1.35",
"mustache": "^4.2.0",
"nanoid": "3.3.4",

View File

@ -19,7 +19,9 @@
"test": "jest"
},
"jest": {
"modulePaths": ["node_modules"]
"modulePaths": [
"node_modules"
]
},
"dependencies": {
"@aws-sdk/credential-providers": "3.525.0",
@ -50,6 +52,7 @@
"js-yaml": "^4.1.0",
"json-bigint": "^1.0.0",
"lodash": "^4.17.21",
"lossless-json": "^4.0.1",
"mime-types": "^2.1.35",
"mustache": "^4.2.0",
"nanoid": "3.3.4",

View File

@ -16,13 +16,21 @@ const {
sanitizeDirectoryName
} = require('../utils/filesystem');
const { openCollectionDialog } = require('../app/collections');
const { generateUidBasedOnHash, stringifyJson, safeParseJSON, safeStringifyJSON } = require('../utils/common');
const { generateUidBasedOnHash, safeParseJSON, safeStringifyJSON } = require('../utils/common');
const { moveRequestUid, deleteRequestUid } = require('../cache/requestUids');
const { deleteCookiesForDomain, getDomainsWithCookies } = require('../utils/cookies');
const EnvironmentSecretsStore = require('../store/env-secrets');
const environmentSecretsStore = new EnvironmentSecretsStore();
const stringifyJson = async (str) => {
try {
return JSON.stringify(str, null, 2);
} catch (err) {
return Promise.reject(err);
}
};
const envHasSecrets = (environment = {}) => {
const secrets = _.filter(environment.variables, (v) => v.secret);

View File

@ -1,6 +1,7 @@
const URL = require('url');
const Socket = require('net').Socket;
const axios = require('axios');
const { safeStringifyJSON } = require('../../utils/common');
const connectionCache = new Map(); // Cache to store checkConnection() results
const LOCAL_IPV6 = '::1';
@ -49,7 +50,22 @@ const checkConnection = (host, port) =>
*/
function makeAxiosInstance() {
/** @type {axios.AxiosInstance} */
const instance = axios.create();
const instance = axios.create({
transformRequest: function transformRequest(data, headers) {
const isObject = (thing) => thing !== null && typeof thing === 'object';
const hasJSONContentType = () => {
const contentType = headers?.['Content-Type'] || headers?.['content-type'] || '';
return contentType.includes('application/json');
};
if (isObject(data) && hasJSONContentType()) {
headers.setContentType('application/json', false);
return safeStringifyJSON(data);
}
axios.defaults.transformRequest.forEach((tr) => tr(data, headers));
}
});
instance.interceptors.request.use(async (config) => {
const url = URL.parse(config.url);

View File

@ -16,7 +16,7 @@ const prepareRequest = require('./prepare-request');
const prepareCollectionRequest = require('./prepare-collection-request');
const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request');
const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token');
const { uuid } = require('../../utils/common');
const { uuid, parseJson } = require('../../utils/common');
const interpolateVars = require('./interpolate-vars');
const { interpolateString } = require('./interpolate-string');
const { sortFolder, getAllRequestsInFolderRecursively } = require('./helper');
@ -285,7 +285,7 @@ const parseDataFromResponse = (response) => {
// Filter out ZWNBSP character
// https://gist.github.com/antic183/619f42b559b78028d1fe9e7ae8a1352d
data = data.replace(/^\uFEFF/, '');
data = JSON.parse(data);
data = parseJson(data);
} catch {}
return { data, dataBuffer };

View File

@ -1,5 +1,6 @@
const { interpolate } = require('@usebruno/common');
const { each, forOwn, cloneDeep, find } = require('lodash');
const { each, forOwn, cloneDeep } = require('lodash');
const { parseJson, stringifyJson } = require('../../utils/common');
const getContentType = (headers = {}) => {
let contentType = '';
@ -61,9 +62,9 @@ const interpolateVars = (request, envVars = {}, runtimeVariables = {}, processEn
if (contentType.includes('json')) {
if (typeof request.data === 'object') {
try {
let parsed = JSON.stringify(request.data);
let parsed = stringifyJson(request.data);
parsed = _interpolate(parsed);
request.data = JSON.parse(parsed);
request.data = parseJson(parsed);
} catch (err) {}
}
@ -75,9 +76,9 @@ const interpolateVars = (request, envVars = {}, runtimeVariables = {}, processEn
} else if (contentType === 'application/x-www-form-urlencoded') {
if (typeof request.data === 'object') {
try {
let parsed = JSON.stringify(request.data);
let parsed = stringifyJson(request.data);
parsed = _interpolate(parsed);
request.data = JSON.parse(parsed);
request.data = parseJson(parsed);
} catch (err) {}
}
} else {

View File

@ -1,7 +1,7 @@
const os = require('os');
const { get, each, filter, extend, compact } = require('lodash');
const decomment = require('decomment');
var JSONbig = require('json-bigint');
const { parseJson } = require('../../utils/common');
const FormData = require('form-data');
const fs = require('fs');
const path = require('path');
@ -349,7 +349,7 @@ const prepareRequest = (item, collection) => {
jsonBody = request?.body?.json;
}
try {
axiosRequest.data = JSONbig.parse(jsonBody);
axiosRequest.data = parseJson(jsonBody);
} catch (error) {
axiosRequest.data = jsonBody;
}

View File

@ -1,4 +1,5 @@
const { customAlphabet } = require('nanoid');
const { stringify, parse, LosslessNumber } = require('lossless-json');
// a customized version of nanoid without using _ and -
const uuid = () => {
@ -9,25 +10,21 @@ const uuid = () => {
return customNanoId();
};
const stringifyJson = async (str) => {
try {
return JSON.stringify(str, null, 2);
} catch (err) {
return Promise.reject(err);
}
const stringifyJson = (data) => {
return stringify(data);
};
const parseJson = async (obj) => {
try {
return JSON.parse(obj);
} catch (err) {
return Promise.reject(err);
}
const parseJson = (data) => {
return parse(data, null, (value) => {
// By default, this will return the LosslessNumber object for big ints
// need to convert it into a number
return new LosslessNumber(value).valueOf();
});
};
const safeStringifyJSON = (data) => {
try {
return JSON.stringify(data);
return stringify(data);
} catch (e) {
return data;
}
@ -35,7 +32,11 @@ const safeStringifyJSON = (data) => {
const safeParseJSON = (data) => {
try {
return JSON.parse(data);
return parse(data, null, (value) => {
// By default, this will return the LosslessNumber object for big ints
// need to convert it into a number
return new LosslessNumber(value).valueOf();
});
} catch (e) {
return data;
}

View File

@ -0,0 +1,34 @@
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
}
}
assert {
res.status: eq 200
}

View File

@ -8,4 +8,4 @@ get {
url: {{host}}/api/echo/bom-json-test
body: none
auth: none
}
}

View File

@ -1,7 +1,7 @@
meta {
name: echo json
type: http
seq: 1
seq: 2
}
post {

View File

@ -1,7 +1,7 @@
meta {
name: echo plaintext
type: http
seq: 2
seq: 3
}
post {

View File

@ -1,7 +1,7 @@
meta {
name: echo xml parsed
type: http
seq: 3
seq: 4
}
post {

View File

@ -1,7 +1,7 @@
meta {
name: echo xml raw
type: http
seq: 4
seq: 5
}
post {