mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-27 14:44:07 +00:00
Merge pull request #4366 from Pragadesh-45/fix/import-curl
Feat: Enhance curl parsing for multipart form data
This commit is contained in:
@@ -183,7 +183,13 @@ const curlToJson = (curlCommand) => {
|
||||
|
||||
if (request.query) {
|
||||
requestJson.queries = getQueries(request);
|
||||
} else if (request.multipartUploads || request.isDataBinary) {
|
||||
} else if (request.multipartUploads) {
|
||||
requestJson.data = request.multipartUploads;
|
||||
if (!requestJson.headers) {
|
||||
requestJson.headers = {};
|
||||
}
|
||||
requestJson.headers['Content-Type'] = 'multipart/form-data';
|
||||
} else if (request.isDataBinary) {
|
||||
Object.assign(requestJson, getFilesString(request));
|
||||
} else if (typeof request.data === 'string' || typeof request.data === 'number') {
|
||||
Object.assign(requestJson, getDataString(request));
|
||||
|
||||
@@ -37,7 +37,8 @@ const parseCurlCommand = (curlCommand) => {
|
||||
alias: {
|
||||
H: 'header',
|
||||
A: 'user-agent',
|
||||
u: 'user'
|
||||
u: 'user',
|
||||
F: 'form'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -95,17 +96,31 @@ const parseCurlCommand = (curlCommand) => {
|
||||
cookieString = parsedArguments.cookie;
|
||||
}
|
||||
let multipartUploads;
|
||||
if (parsedArguments.F) {
|
||||
multipartUploads = {};
|
||||
if (!Array.isArray(parsedArguments.F)) {
|
||||
parsedArguments.F = [parsedArguments.F];
|
||||
}
|
||||
parsedArguments.F.forEach((multipartArgument) => {
|
||||
// input looks like key=value. value could be json or a file path prepended with an @
|
||||
const splitArguments = multipartArgument.split('=', 2);
|
||||
const key = splitArguments[0];
|
||||
const value = splitArguments[1];
|
||||
multipartUploads[key] = value;
|
||||
// Handle multipart form data specified via -F or --form flags
|
||||
// Example: curl -F 'id=123' -F 'file=@/path/to/file.txt'
|
||||
if (parsedArguments.F || parsedArguments.form) {
|
||||
multipartUploads = [];
|
||||
const formArgs = parsedArguments.F || parsedArguments.form;
|
||||
const formArray = Array.isArray(formArgs) ? formArgs : [formArgs];
|
||||
|
||||
formArray.forEach((multipartArgument) => {
|
||||
// Parse each form field using regex:
|
||||
// - Group 1: Field name before =
|
||||
// - Group 2: Value in quotes after = (for text fields)
|
||||
// - Group 3: Value after @ (for file fields)
|
||||
const match = multipartArgument.match(/^([^=]+)=(?:@?"([^"]*)"|([^@]*))?$/);
|
||||
if (match) {
|
||||
const key = match[1];
|
||||
const value = match[2] || match[3] || '';
|
||||
const isFile = multipartArgument.includes('@');
|
||||
|
||||
multipartUploads.push({
|
||||
name: key,
|
||||
value: value,
|
||||
type: isFile ? 'file' : 'text',
|
||||
enabled: true
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (cookieString) {
|
||||
|
||||
145
packages/bruno-app/src/utils/curl/parse-curl.spec.js
Normal file
145
packages/bruno-app/src/utils/curl/parse-curl.spec.js
Normal file
@@ -0,0 +1,145 @@
|
||||
const { describe, it, expect } = require('@jest/globals');
|
||||
import parseCurlCommand from './parse-curl';
|
||||
|
||||
describe('parseCurlCommand', () => {
|
||||
describe('basic functionality', () => {
|
||||
it('should handle basic GET request', () => {
|
||||
const result = parseCurlCommand('curl https://api.example.com/users');
|
||||
expect(result).toEqual({
|
||||
url: 'https://api.example.com/users',
|
||||
urlWithoutQuery: 'https://api.example.com/users',
|
||||
method: 'get'
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse explicit POST method', () => {
|
||||
const result = parseCurlCommand('curl -X POST https://api.example.com/users');
|
||||
expect(result).toEqual({
|
||||
url: 'https://api.example.com/users',
|
||||
urlWithoutQuery: 'https://api.example.com/users',
|
||||
method: 'post'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('headers handling', () => {
|
||||
it('should parse multiple headers', () => {
|
||||
const result = parseCurlCommand(
|
||||
`curl -H 'Content-Type: application/json' -H 'Authorization: Bearer token' https://api.example.com`
|
||||
);
|
||||
expect(result).toEqual({
|
||||
url: 'https://api.example.com',
|
||||
urlWithoutQuery: 'https://api.example.com',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: 'Bearer token'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse user-agent', () => {
|
||||
const result = parseCurlCommand(`curl -A 'Custom Agent' https://api.example.com`);
|
||||
expect(result).toEqual({
|
||||
url: 'https://api.example.com',
|
||||
urlWithoutQuery: 'https://api.example.com',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'User-Agent': 'Custom Agent'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('auth handling', () => {
|
||||
it('should parse basic auth', () => {
|
||||
const result = parseCurlCommand(`curl -u user:pass https://api.example.com`);
|
||||
expect(result).toEqual({
|
||||
url: 'https://api.example.com',
|
||||
urlWithoutQuery: 'https://api.example.com',
|
||||
method: 'get',
|
||||
auth: {
|
||||
mode: 'basic',
|
||||
basic: {
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('data handling', () => {
|
||||
it('should parse POST data', () => {
|
||||
const result = parseCurlCommand(`curl -d 'foo=bar&baz=qux' https://api.example.com`);
|
||||
expect(result).toEqual({
|
||||
url: 'https://api.example.com',
|
||||
urlWithoutQuery: 'https://api.example.com',
|
||||
method: 'post',
|
||||
data: 'foo=bar&baz=qux'
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle data-binary', () => {
|
||||
const result = parseCurlCommand(`curl --data-binary '@file.json' https://api.example.com`);
|
||||
expect(result).toEqual({
|
||||
url: 'https://api.example.com',
|
||||
urlWithoutQuery: 'https://api.example.com',
|
||||
method: 'post',
|
||||
data: '@file.json',
|
||||
isDataBinary: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('form data handling', () => {
|
||||
it('should parse complex form data with multiple fields and file upload', () => {
|
||||
const curlCommand = `curl --location 'https://echo.usebruno.com/5cf47630-8d45-4fd3-937b-c4b1dea70c6d' \
|
||||
--form 'id="1"' \
|
||||
--form 'documentid="ADMINN_ID"' \
|
||||
--form 'appoinID="12376"' \
|
||||
--form 'autoclose="false"' \
|
||||
--form 'fileData=@"/path/to/file"'`;
|
||||
|
||||
const result = parseCurlCommand(curlCommand);
|
||||
|
||||
expect(result).toEqual({
|
||||
url: 'https://echo.usebruno.com/5cf47630-8d45-4fd3-937b-c4b1dea70c6d',
|
||||
urlWithoutQuery: 'https://echo.usebruno.com/5cf47630-8d45-4fd3-937b-c4b1dea70c6d',
|
||||
method: 'post',
|
||||
multipartUploads: [
|
||||
{
|
||||
name: 'id',
|
||||
value: '1',
|
||||
type: 'text',
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
name: 'documentid',
|
||||
value: 'ADMINN_ID',
|
||||
type: 'text',
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
name: 'appoinID',
|
||||
value: '12376',
|
||||
type: 'text',
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
name: 'autoclose',
|
||||
value: 'false',
|
||||
type: 'text',
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
name: 'fileData',
|
||||
value: '/path/to/file',
|
||||
type: 'file',
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -330,8 +330,9 @@ ${indentString(body.sparql)}
|
||||
}
|
||||
|
||||
if (item.type === 'file') {
|
||||
let filepaths = item.value || [];
|
||||
let filestr = filepaths.join('|');
|
||||
const filepaths = Array.isArray(item.value) ? item.value : [];
|
||||
const filestr = filepaths.join('|');
|
||||
|
||||
const value = `@file(${filestr})`;
|
||||
return `${enabled}${item.name}: ${value}${contentType}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user