mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
fix: update logic for checking formdata instances (#6643)
* fix: update logic for checking formdata instance * fix: isFormData logic update * fix: review comment fix, add isFormData to @usebruno/common package * fix: review comment fix
This commit is contained in:
1
package-lock.json
generated
1
package-lock.json
generated
@@ -33081,6 +33081,7 @@
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"babel-jest": "^29.7.0",
|
||||
"form-data": "^4.0.0",
|
||||
"is-ip": "^5.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"rollup": "3.29.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { interpolate } = require('@usebruno/common');
|
||||
const { each, forOwn, cloneDeep, find } = require('lodash');
|
||||
const FormData = require('form-data');
|
||||
const { isFormData } = require('@usebruno/common').utils;
|
||||
|
||||
const getContentType = (headers = {}) => {
|
||||
let contentType = '';
|
||||
@@ -87,7 +87,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
}));
|
||||
}
|
||||
} else if (contentType === 'multipart/form-data') {
|
||||
if (Array.isArray(request?.data) && !(request.data instanceof FormData)) {
|
||||
if (Array.isArray(request?.data) && !isFormData(request.data)) {
|
||||
try {
|
||||
request.data = request?.data?.map((d) => ({
|
||||
...d,
|
||||
|
||||
@@ -3,7 +3,6 @@ const chalk = require('chalk');
|
||||
const decomment = require('decomment');
|
||||
const fs = require('fs');
|
||||
const { forOwn, isUndefined, isNull, each, extend, get, compact } = require('lodash');
|
||||
const FormData = require('form-data');
|
||||
const prepareRequest = require('./prepare-request');
|
||||
const interpolateVars = require('./interpolate-vars');
|
||||
const { interpolateString } = require('./interpolate-string');
|
||||
@@ -25,7 +24,7 @@ const { NtlmClient } = require('axios-ntlm');
|
||||
const { addDigestInterceptor } = require('@usebruno/requests');
|
||||
const { getCACertificates } = require('@usebruno/requests');
|
||||
const { getOAuth2Token } = require('../utils/oauth2');
|
||||
const { encodeUrl, buildFormUrlEncodedPayload, extractPromptVariables } = require('@usebruno/common').utils;
|
||||
const { encodeUrl, buildFormUrlEncodedPayload, extractPromptVariables, isFormData } = require('@usebruno/common').utils;
|
||||
|
||||
const onConsoleLog = (type, args) => {
|
||||
console[type](...args);
|
||||
@@ -429,7 +428,7 @@ const runSingleRequest = async function (
|
||||
}
|
||||
|
||||
if (contentTypeHeader && request.headers[contentTypeHeader] === 'multipart/form-data') {
|
||||
if (!(request?.data instanceof FormData)) {
|
||||
if (!isFormData(request?.data)) {
|
||||
request._originalMultipartData = request.data;
|
||||
request.collectionPath = collectionPath;
|
||||
let form = createFormData(request.data, collectionPath);
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"babel-jest": "^29.7.0",
|
||||
"form-data": "^4.0.0",
|
||||
"is-ip": "^5.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"rollup": "3.29.5",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { buildFormUrlEncodedPayload } from './form-data';
|
||||
import { buildFormUrlEncodedPayload, isFormData } from './form-data';
|
||||
import FormData from 'form-data';
|
||||
|
||||
describe('buildFormUrlEncodedPayload', () => {
|
||||
it('should handle single key-value pair', () => {
|
||||
@@ -110,3 +111,53 @@ describe('buildFormUrlEncodedPayload', () => {
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFormData', () => {
|
||||
it('should return true for objects with FormData constructor name', () => {
|
||||
const mockFormData = {
|
||||
constructor: { name: 'FormData' }
|
||||
};
|
||||
expect(isFormData(mockFormData)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for null', () => {
|
||||
expect(isFormData(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for undefined', () => {
|
||||
expect(isFormData(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for plain objects', () => {
|
||||
expect(isFormData({})).toBe(false);
|
||||
expect(isFormData({ key: 'value' })).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for arrays', () => {
|
||||
expect(isFormData([])).toBe(false);
|
||||
expect(isFormData([1, 2, 3])).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for primitives', () => {
|
||||
expect(isFormData('string')).toBe(false);
|
||||
expect(isFormData(123)).toBe(false);
|
||||
expect(isFormData(true)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for objects with different constructor names', () => {
|
||||
class CustomClass {}
|
||||
const customObj = new CustomClass();
|
||||
expect(isFormData(customObj)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for objects without constructor', () => {
|
||||
const obj = Object.create(null);
|
||||
expect(isFormData(obj)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true for actual FormData instance from form-data library', () => {
|
||||
const formData = new FormData();
|
||||
formData.append('key', 'value');
|
||||
expect(isFormData(formData)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,3 +31,15 @@ export const buildFormUrlEncodedPayload = (params: Array<{ name: string; value:
|
||||
|
||||
return resultParams.toString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if the given object is a FormData instance.
|
||||
* Supports native FormData (Node 18+, browser) and the 'form-data' npm package.
|
||||
* @param obj - Object to check.
|
||||
* @returns True if obj is a FormData instance, false otherwise.
|
||||
*/
|
||||
export const isFormData = (obj: unknown): boolean => {
|
||||
// Check constructor name (works for both native FormData and form-data npm package)
|
||||
// todo: checking constructor.name can produce false positives for objects that have a constructor.name property set to 'FormData', but this is rare.
|
||||
return obj?.constructor?.name === 'FormData';
|
||||
};
|
||||
|
||||
@@ -5,7 +5,8 @@ export {
|
||||
} from './url';
|
||||
|
||||
export {
|
||||
buildFormUrlEncodedPayload
|
||||
buildFormUrlEncodedPayload,
|
||||
isFormData
|
||||
} from './form-data';
|
||||
|
||||
export {
|
||||
|
||||
@@ -5,7 +5,6 @@ const qs = require('qs');
|
||||
const decomment = require('decomment');
|
||||
const contentDispositionParser = require('content-disposition');
|
||||
const mime = require('mime-types');
|
||||
const FormData = require('form-data');
|
||||
const { ipcMain } = require('electron');
|
||||
const { each, get, extend, cloneDeep, merge } = require('lodash');
|
||||
const { NtlmClient } = require('axios-ntlm');
|
||||
@@ -36,7 +35,7 @@ const { cookiesStore } = require('../../store/cookies');
|
||||
const registerGrpcEventHandlers = require('./grpc-event-handlers');
|
||||
const { registerWsEventHandlers } = require('./ws-event-handlers');
|
||||
const { getCertsAndProxyConfig } = require('./cert-utils');
|
||||
const { buildFormUrlEncodedPayload } = require('@usebruno/common').utils;
|
||||
const { buildFormUrlEncodedPayload, isFormData } = require('@usebruno/common').utils;
|
||||
|
||||
const ERROR_OCCURRED_WHILE_EXECUTING_REQUEST = 'Error occurred while executing the request!';
|
||||
|
||||
@@ -474,7 +473,7 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
}
|
||||
|
||||
if (contentTypeHeader && request.headers[contentTypeHeader] === 'multipart/form-data') {
|
||||
if (!(request.data instanceof FormData)) {
|
||||
if (!isFormData(request.data)) {
|
||||
request._originalMultipartData = request.data;
|
||||
request.collectionPath = collectionPath;
|
||||
let form = createFormData(request.data, collectionPath);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { interpolate } = require('@usebruno/common');
|
||||
const { each, forOwn, cloneDeep } = require('lodash');
|
||||
const FormData = require('form-data');
|
||||
const { isFormData } = require('@usebruno/common').utils;
|
||||
|
||||
const getContentType = (headers = {}) => {
|
||||
let contentType = '';
|
||||
@@ -132,7 +132,7 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
}));
|
||||
}
|
||||
} else if (contentType === 'multipart/form-data') {
|
||||
if (Array.isArray(request?.data) && !(request.data instanceof FormData)) {
|
||||
if (Array.isArray(request?.data) && !isFormData(request.data)) {
|
||||
try {
|
||||
request.data = request?.data?.map((d) => ({
|
||||
...d,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const { customAlphabet } = require('nanoid');
|
||||
const iconv = require('iconv-lite');
|
||||
const { cloneDeep } = require('lodash');
|
||||
const FormData = require('form-data');
|
||||
const { formatMultipartData } = require('./form-data');
|
||||
const { isFormData } = require('@usebruno/common').utils;
|
||||
|
||||
// a customized version of nanoid without using _ and -
|
||||
const uuid = () => {
|
||||
@@ -135,7 +135,7 @@ const parseDataFromRequest = (request) => {
|
||||
// File uploads are redacted, multipart FormData is formatted from original data for readability, and other types are stringified as-is.
|
||||
if (request.mode === 'file') {
|
||||
requestDataString = '<request body redacted>';
|
||||
} else if (request?.data instanceof FormData && Array.isArray(request._originalMultipartData)) {
|
||||
} else if (isFormData(request?.data) && Array.isArray(request._originalMultipartData)) {
|
||||
const boundary = request.data._boundary || 'boundary';
|
||||
requestDataString = formatMultipartData(request._originalMultipartData, boundary);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user