mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-15 11:51:30 +00:00
feat: added multipart data formatting in timeline (#6185)
refactor: remove escapeHeaderValue function and enhance formatMultipartData utility
This commit is contained in:
@@ -1,6 +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');
|
||||
|
||||
// a customized version of nanoid without using _ and -
|
||||
const uuid = () => {
|
||||
@@ -128,7 +130,18 @@ const parseDataFromResponse = (response, disableParsingResponseJson = false) =>
|
||||
};
|
||||
|
||||
const parseDataFromRequest = (request) => {
|
||||
const requestDataString = request.mode == 'file'? "<request body redacted>": (typeof request?.data === 'string' ? request?.data : safeStringifyJSON(request?.data));
|
||||
let requestDataString;
|
||||
|
||||
// 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)) {
|
||||
const boundary = request.data._boundary || 'boundary';
|
||||
requestDataString = formatMultipartData(request._originalMultipartData, boundary);
|
||||
} else {
|
||||
requestDataString = typeof request?.data === 'string' ? request?.data : safeStringifyJSON(request?.data);
|
||||
}
|
||||
|
||||
const requestCopy = cloneDeep(request);
|
||||
if (!requestCopy.data) {
|
||||
return { data: null, dataBuffer: null };
|
||||
|
||||
@@ -3,6 +3,61 @@ const FormData = require('form-data');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const formatMultipartData = (multipartData, boundary) => {
|
||||
if (!Array.isArray(multipartData) || multipartData.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const normalizeBoundary = (b) => {
|
||||
const value = b || 'boundary';
|
||||
return value.replace(/^--+/, '').replace(/--+$/, '');
|
||||
};
|
||||
|
||||
const getFileName = (filePath) => {
|
||||
if (typeof filePath === 'string' && filePath.trim()) {
|
||||
return path.basename(filePath) || 'file';
|
||||
}
|
||||
return 'file';
|
||||
};
|
||||
|
||||
const formatValue = (value) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((v) => String(v ?? '')).join(', ');
|
||||
}
|
||||
return String(value ?? '');
|
||||
};
|
||||
|
||||
const boundaryValue = normalizeBoundary(boundary);
|
||||
const parts = [];
|
||||
|
||||
multipartData.forEach((field) => {
|
||||
if (!field || !field.name) return;
|
||||
|
||||
parts.push(`----${boundaryValue}`);
|
||||
parts.push('Content-Disposition: form-data');
|
||||
|
||||
if (field.type === 'file') {
|
||||
const filePaths = Array.isArray(field.value) ? field.value : (field.value ? [field.value] : ['']);
|
||||
filePaths.forEach((filePath) => {
|
||||
parts.push(`----${boundaryValue}`);
|
||||
parts.push('Content-Disposition: form-data');
|
||||
const fileName = getFileName(filePath);
|
||||
parts.push(`name: ${field.name}`);
|
||||
parts.push(`value: [File: ${fileName}]`);
|
||||
parts.push('');
|
||||
});
|
||||
} else {
|
||||
const value = formatValue(field.value);
|
||||
parts.push(`name: ${field.name}`);
|
||||
parts.push(`value: ${value}`);
|
||||
parts.push('');
|
||||
}
|
||||
});
|
||||
|
||||
parts.push(`----${boundaryValue}--`);
|
||||
return parts.join('\n');
|
||||
};
|
||||
|
||||
const createFormData = (data, collectionPath) => {
|
||||
// make axios work in node using form data
|
||||
// reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427
|
||||
@@ -38,5 +93,6 @@ const createFormData = (data, collectionPath) => {
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createFormData
|
||||
createFormData,
|
||||
formatMultipartData
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const { flattenDataForDotNotation } = require('../../src/utils/common');
|
||||
const { flattenDataForDotNotation, parseDataFromRequest } = require('../../src/utils/common');
|
||||
const FormData = require('form-data');
|
||||
|
||||
describe('utils: flattenDataForDotNotation', () => {
|
||||
test('Flatten a simple object with dot notation', () => {
|
||||
@@ -82,4 +83,24 @@ describe('utils: flattenDataForDotNotation', () => {
|
||||
|
||||
expect(flattenDataForDotNotation(input)).toEqual(expectedOutput);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('utils: parseDataFromRequest', () => {
|
||||
test('should format multipart FormData', () => {
|
||||
const formData = new FormData();
|
||||
formData._boundary = 'boundary123';
|
||||
const request = {
|
||||
data: formData,
|
||||
_originalMultipartData: [
|
||||
{ name: 'description', type: 'text', value: 'dfv' },
|
||||
{ name: 'file', type: 'file', value: ['Dumy.xml'] }
|
||||
],
|
||||
headers: {}
|
||||
};
|
||||
|
||||
const result = parseDataFromRequest(request);
|
||||
expect(result.data).toContain('name: description');
|
||||
expect(result.data).toContain('value: dfv');
|
||||
expect(result.data).toContain('value: [File: Dumy.xml]');
|
||||
});
|
||||
});
|
||||
|
||||
46
packages/bruno-electron/tests/utils/form-data.spec.js
Normal file
46
packages/bruno-electron/tests/utils/form-data.spec.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const { formatMultipartData } = require('../../src/utils/form-data');
|
||||
|
||||
describe('utils: formatMultipartData', () => {
|
||||
test('should format text field', () => {
|
||||
const data = [{ name: 'description', type: 'text', value: 'dfv' }];
|
||||
const result = formatMultipartData(data, 'boundary');
|
||||
|
||||
expect(result).toContain('----boundary');
|
||||
expect(result).toContain('Content-Disposition: form-data');
|
||||
expect(result).toContain('name: description');
|
||||
expect(result).toContain('value: dfv');
|
||||
expect(result).toContain('----boundary--');
|
||||
});
|
||||
|
||||
test('should format file field', () => {
|
||||
const data = [{ name: 'file', type: 'file', value: ['Dumy.xml'] }];
|
||||
const result = formatMultipartData(data, 'boundary');
|
||||
|
||||
expect(result).toContain('name: file');
|
||||
expect(result).toContain('value: [File: Dumy.xml]');
|
||||
});
|
||||
|
||||
test('should format multiple fields', () => {
|
||||
const data = [
|
||||
{ name: 'description', type: 'text', value: 'dfv' },
|
||||
{ name: 'file', type: 'file', value: ['Dumy.xml'] }
|
||||
];
|
||||
const result = formatMultipartData(data, 'boundary');
|
||||
|
||||
expect(result).toContain('name: description');
|
||||
expect(result).toContain('value: dfv');
|
||||
expect(result).toContain('name: file');
|
||||
expect(result).toContain('value: [File: Dumy.xml]');
|
||||
});
|
||||
|
||||
test('should return empty string for invalid input', () => {
|
||||
expect(formatMultipartData([], 'boundary')).toBe('');
|
||||
expect(formatMultipartData(null, 'boundary')).toBe('');
|
||||
});
|
||||
|
||||
test('should normalize boundary', () => {
|
||||
const data = [{ name: 'field', type: 'text', value: 'value' }];
|
||||
expect(formatMultipartData(data, '--boundary')).toContain('----boundary');
|
||||
expect(formatMultipartData(data, 'boundary--')).toContain('----boundary');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user