diff --git a/packages/bruno-cli/src/runner/interpolate-vars.js b/packages/bruno-cli/src/runner/interpolate-vars.js index 97d659908..8e0ff300c 100644 --- a/packages/bruno-cli/src/runner/interpolate-vars.js +++ b/packages/bruno-cli/src/runner/interpolate-vars.js @@ -86,11 +86,12 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc } catch (err) {} } } else if (contentType === 'multipart/form-data') { - if (typeof request.data === 'object' && !(request?.data instanceof FormData)) { + if (Array.isArray(request?.data) && !(request.data instanceof FormData)) { try { - forOwn(request?.data, (value, key) => { - request.data[key] = _interpolate(value); - }); + request.data = request?.data?.map(d => ({ + ...d, + value: _interpolate(d?.value) + })); } catch (err) {} } } else { diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index dd9d450f0..07af15c22 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -19,8 +19,9 @@ const { makeAxiosInstance } = require('../utils/axios-instance'); const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-helper'); const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../utils/proxy-util'); const path = require('path'); -const { createFormData, parseDataFromResponse } = require('../utils/common'); +const { parseDataFromResponse } = require('../utils/common'); const { getCookieStringForUrl, saveCookies, shouldUseCookies } = require('../utils/cookies'); +const { createFormData } = require('../utils/form-data'); const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/; const onConsoleLog = (type, args) => { diff --git a/packages/bruno-cli/src/utils/common.js b/packages/bruno-cli/src/utils/common.js index dd45be501..2505225f1 100644 --- a/packages/bruno-cli/src/utils/common.js +++ b/packages/bruno-cli/src/utils/common.js @@ -20,30 +20,6 @@ const rpad = (str, width) => { return paddedStr; }; -const createFormData = (datas, collectionPath) => { - // make axios work in node using form data - // reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427 - const form = new FormData(); - forOwn(datas, (value, key) => { - if (typeof value == 'string') { - form.append(key, value); - return; - } - - const filePaths = value || []; - filePaths?.forEach?.((filePath) => { - let trimmedFilePath = filePath.trim(); - - if (!path.isAbsolute(trimmedFilePath)) { - trimmedFilePath = path.join(collectionPath, trimmedFilePath); - } - - form.append(key, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath)); - }); - }); - return form; -}; - const parseDataFromResponse = (response, disableParsingResponseJson = false) => { // Parse the charset from content type: https://stackoverflow.com/a/33192813 const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(response.headers['content-type'] || ''); @@ -73,6 +49,5 @@ const parseDataFromResponse = (response, disableParsingResponseJson = false) => module.exports = { lpad, rpad, - createFormData, parseDataFromResponse }; diff --git a/packages/bruno-cli/src/utils/form-data.js b/packages/bruno-cli/src/utils/form-data.js index f5eec945f..eab5d5824 100644 --- a/packages/bruno-cli/src/utils/form-data.js +++ b/packages/bruno-cli/src/utils/form-data.js @@ -8,13 +8,16 @@ const createFormData = (data, collectionPath) => { // reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427 const form = new FormData(); forEach(data, (datum) => { - const { name, type, value } = datum; - + const { name, type, value, contentType } = datum; + let options = {}; + if (contentType) { + options.contentType = contentType; + } if (type === 'text') { if (Array.isArray(value)) { - value.forEach((val) => form.append(name, val)); + value.forEach((val) => form.append(name, val, options)); } else { - form.append(name, value); + form.append(name, value, options); } return; } @@ -23,12 +26,11 @@ const createFormData = (data, collectionPath) => { const filePaths = value || []; filePaths.forEach((filePath) => { let trimmedFilePath = filePath.trim(); - console.log(trimmedFilePath, collectionPath); if (!path.isAbsolute(trimmedFilePath)) { trimmedFilePath = path.join(collectionPath, trimmedFilePath); } - - form.append(name, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath)); + options.filename = path.basename(trimmedFilePath); + form.append(name, fs.createReadStream(trimmedFilePath), options); }); } }); diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index 59f494416..d9833f59a 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -86,11 +86,12 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc } catch (err) {} } } else if (contentType === 'multipart/form-data') { - if (typeof request.data === 'object' && !(request.data instanceof FormData)) { + if (Array.isArray(request?.data) && !(request.data instanceof FormData)) { try { - forOwn(request?.data, (value, key) => { - request.data[key] = _interpolate(value); - }); + request.data = request?.data?.map(d => ({ + ...d, + value: _interpolate(d?.value) + })); } catch (err) {} } } else { diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index de6ed1632..f2616a9da 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-request.js @@ -249,7 +249,7 @@ const prepareRequest = (item, collection) => { axiosRequest.headers['content-type'] = 'multipart/form-data'; } const enabledParams = filter(request.body.multipartForm, (p) => p.enabled); - axiosRequest.data = createFormData(enabledParams); + axiosRequest.data = enabledParams; } if (request.body.mode === 'graphql') { diff --git a/packages/bruno-electron/src/utils/form-data.js b/packages/bruno-electron/src/utils/form-data.js index 81a3c5621..f20371128 100644 --- a/packages/bruno-electron/src/utils/form-data.js +++ b/packages/bruno-electron/src/utils/form-data.js @@ -27,13 +27,16 @@ const createFormData = (data, collectionPath) => { // reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427 const form = new FormData(); forEach(data, (datum) => { - const { name, type, value } = datum; - + const { name, type, value, contentType } = datum; + let options = {}; + if (contentType) { + options.contentType = contentType; + } if (type === 'text') { if (Array.isArray(value)) { - value.forEach((val) => form.append(name, val)); + value.forEach((val) => form.append(name, val, options)); } else { - form.append(name, value); + form.append(name, value, options); } return; } @@ -42,12 +45,11 @@ const createFormData = (data, collectionPath) => { const filePaths = value || []; filePaths.forEach((filePath) => { let trimmedFilePath = filePath.trim(); - if (!path.isAbsolute(trimmedFilePath)) { trimmedFilePath = path.join(collectionPath, trimmedFilePath); } - - form.append(name, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath)); + options.filename = path.basename(trimmedFilePath); + form.append(name, fs.createReadStream(trimmedFilePath), options); }); } }); diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index 132443890..228691c1b 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -162,7 +162,7 @@ const mapRequestParams = (pairList = [], type) => { const multipartExtractContentType = (pair) => { if (_.isString(pair.value)) { - const match = pair.value.match(/^(.*?)\s*\(Content-Type=(.*?)\)\s*$/); + const match = pair.value.match(/^(.*?)\s*@contentType\((.*?)\)\s*$/); if (match != null && match.length > 2) { pair.value = match[1]; pair.contentType = match[2]; diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js index 03e36c71a..5c8a573b6 100644 --- a/packages/bruno-lang/v2/src/jsonToBru.js +++ b/packages/bruno-lang/v2/src/jsonToBru.js @@ -281,7 +281,7 @@ ${indentString(body.sparql)} .map((item) => { const enabled = item.enabled ? '' : '~'; const contentType = - item.contentType && item.contentType !== '' ? ' (Content-Type=' + item.contentType + ')' : ''; + item.contentType && item.contentType !== '' ? ' @contentType(' + item.contentType + ')' : ''; if (item.type === 'text') { return `${enabled}${item.name}: ${getValueString(item.value)}${contentType}`; diff --git a/packages/bruno-tests/collection/multipart/mixed-content-types.bru b/packages/bruno-tests/collection/multipart/mixed-content-types.bru index 45a1cdd18..29ed04ba1 100644 --- a/packages/bruno-tests/collection/multipart/mixed-content-types.bru +++ b/packages/bruno-tests/collection/multipart/mixed-content-types.bru @@ -12,13 +12,13 @@ post { body:multipart-form { param1: test - param2: {"test":"i am json"} (Content-Type=application/json) + param2: {"test":"i am json"} @contentType(application/json) param3: @file(multipart/small.png) } assert { res.status: eq 200 - res.body.find(p=>p.name === 'param1').contentType: isUndefined + res.body.find(p=>p.name === 'param1').contentType: isUndefined res.body.find(p=>p.name === 'param2').contentType: eq application/json res.body.find(p=>p.name === 'param3').contentType: eq image/png } diff --git a/packages/bruno-tests/src/index.js b/packages/bruno-tests/src/index.js index 0bff6ab75..a09cb434b 100644 --- a/packages/bruno-tests/src/index.js +++ b/packages/bruno-tests/src/index.js @@ -5,6 +5,7 @@ const formDataParser = require('./multipart/form-data-parser'); const authRouter = require('./auth'); const echoRouter = require('./echo'); const xmlParser = require('./utils/xmlParser'); +const multipartRouter = require('./multipart'); const app = new express(); const port = process.env.PORT || 8080;