Select the type of your existing collection :
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
index 0737cb133..2807bc11b 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
@@ -1041,13 +1041,16 @@ export const browseDirectory = () => (dispatch, getState) => {
export const browseFiles =
(filters = [], properties = ['multiSelections']) =>
- (dispatch, getState) => {
+ (_dispatch, _getState) => {
const { ipcRenderer } = window;
return new Promise((resolve, reject) => {
- ipcRenderer.invoke('renderer:browse-files', undefined, undefined, undefined, filters, properties).then(resolve).catch(reject);
+ ipcRenderer
+ .invoke('renderer:browse-files', filters, properties)
+ .then(resolve)
+ .catch(reject);
});
- };
+};
export const updateBrunoConfig = (brunoConfig, collectionUid) => (dispatch, getState) => {
const state = getState();
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
index 35ccf244c..105901369 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
@@ -865,80 +865,82 @@ export const collectionsSlice = createSlice({
}
}
},
- moveMultipartFormParam: (state, action) => {
- // Ensure item.draft is a deep clone of item if not already present
- if (!item.draft) {
- item.draft = cloneDeep(item);
- }
-
- // Extract payload data
- const { updateReorderedItem } = action.payload;
- const params = item.draft.request.body.multipartForm;
-
- item.draft.request.body.multipartForm = updateReorderedItem.map((uid) => {
- return params.find((param) => param.uid === uid);
- });
- },
- addBinaryFile: (state, action) => {
+ moveMultipartFormParam: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
if (collection) {
const item = findItemInCollection(collection, action.payload.itemUid);
+ if (item && isItemARequest(item)) {
+ // Ensure item.draft is a deep clone of item if not already present
+ if (!item.draft) {
+ item.draft = cloneDeep(item);
+ }
+
+ // Extract payload data
+ const { updateReorderedItem } = action.payload;
+ const params = item.draft.request.body.multipartForm;
+
+ item.draft.request.body.multipartForm = updateReorderedItem.map((uid) => {
+ return params.find((param) => param.uid === uid);
+ });
+ }
+ }
+ },
+ addBinaryFile: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ const item = findItemInCollection(collection, action.payload.itemUid);
+
if (item && isItemARequest(item)) {
if (!item.draft) {
item.draft = cloneDeep(item);
}
item.draft.request.body.binaryFile = item.draft.request.body.binaryFile || [];
-
+
item.draft.request.body.binaryFile.push({
uid: uuid(),
- type: action.payload.type,
- name: '',
- value: [''],
+ filePath: '',
contentType: '',
- enabled: false
+ selected: false
});
}
}
},
updateBinaryFile: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
-
+
if (collection) {
const item = findItemInCollection(collection, action.payload.itemUid);
-
+
if (item && isItemARequest(item)) {
if (!item.draft) {
item.draft = cloneDeep(item);
}
-
- item.draft.request.body.binaryFile = item.draft.request.body.binaryFile.map((p) => {
- p.enabled = false;
- return p;
- });
-
+
const param = find(item.draft.request.body.binaryFile, (p) => p.uid === action.payload.param.uid);
-
+
if (param) {
-
- const contentType = mime.contentType(path.extname(action.payload.param.value[0]));
-
- param.type = action.payload.param.type;
- param.name = action.payload.param.name;
- param.value = action.payload.param.value;
+ const contentType = mime.contentType(path.extname(action.payload.param.filePath));
+ param.filePath = action.payload.param.filePath;
param.contentType = action.payload.param.contentType || contentType || '';
- param.enabled = action.payload.param.enabled;
+ param.selected = action.payload.param.selected;
+
+ item.draft.request.body.binaryFile = item.draft.request.body.binaryFile.map((p) => {
+ p.selected = p.uid === param.uid;
+ return p;
+ });
}
}
}
},
deleteBinaryFile: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
-
+
if (collection) {
const item = findItemInCollection(collection, action.payload.itemUid);
-
+
if (item && isItemARequest(item)) {
if (!item.draft) {
item.draft = cloneDeep(item);
@@ -948,6 +950,10 @@ export const collectionsSlice = createSlice({
item.draft.request.body.binaryFile,
(p) => p.uid !== action.payload.paramUid
);
+
+ if (item.draft.request.body.binaryFile.length > 0) {
+ item.draft.request.body.binaryFile[0].selected = true;
+ }
}
}
},
diff --git a/packages/bruno-app/src/utils/codegenerator/har.js b/packages/bruno-app/src/utils/codegenerator/har.js
index 19e4ea4de..8514588ab 100644
--- a/packages/bruno-app/src/utils/codegenerator/har.js
+++ b/packages/bruno-app/src/utils/codegenerator/har.js
@@ -65,6 +65,23 @@ const createPostData = (body, type) => {
switch (body.mode) {
case 'formUrlEncoded':
+ return {
+ mimeType: contentType,
+ text: new URLSearchParams(
+ body[body.mode]
+ .filter((param) => param.enabled)
+ .reduce((acc, param) => {
+ acc[param.name] = param.value;
+ return acc;
+ }, {})
+ ).toString(),
+ params: body[body.mode]
+ .filter((param) => param.enabled)
+ .map((param) => ({
+ name: param.name,
+ value: param.value
+ }))
+ };
case 'multipartForm':
return {
mimeType: contentType,
@@ -78,18 +95,13 @@ const createPostData = (body, type) => {
};
case 'binaryFile':
const binary = {
- mimeType: 'application/octet-stream',
- // mimeType: body[body.mode].filter((param) => param.enabled)[0].contentType,
+ mimeType: body[body.mode].filter((param) => param.enabled)[0].contentType,
params: body[body.mode]
- .filter((param) => param.enabled)
+ .filter((param) => param.selected)
.map((param) => ({
- name: param.name,
- value: param.value,
- fileName: param.value
+ value: param.filePath,
}))
};
-
- console.log('curl-binary', binary);
return binary;
default:
return {
@@ -100,10 +112,6 @@ const createPostData = (body, type) => {
};
export const buildHarRequest = ({ request, headers, type }) => {
-
- console.log('buildHarRequest', request, headers, type);
-
- console.log('buildHarRequest-postData', createPostData(request.body, type));
return {
method: request.method,
url: encodeURI(request.url),
diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js
index 96e91b78a..db9a92503 100644
--- a/packages/bruno-app/src/utils/collections/index.js
+++ b/packages/bruno-app/src/utils/collections/index.js
@@ -275,11 +275,9 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {}
return map(params, (param) => {
return {
uid: param.uid,
- type: param.type,
- name: param.name,
- value: param.value,
+ filePath: param.filePath,
contentType: param.contentType,
- enabled: param.enabled
+ selected: param.selected
}
});
}
@@ -666,7 +664,7 @@ export const humanizeRequestBodyMode = (mode) => {
break;
}
case 'binaryFile': {
- label = 'Binary File';
+ label = 'File / Binary';
break;
}
case 'formUrlEncoded': {
diff --git a/packages/bruno-app/src/utils/curl/curl-to-json.js b/packages/bruno-app/src/utils/curl/curl-to-json.js
index 5a2c62022..7683b5cda 100644
--- a/packages/bruno-app/src/utils/curl/curl-to-json.js
+++ b/packages/bruno-app/src/utils/curl/curl-to-json.js
@@ -102,30 +102,24 @@ function getFilesString(request) {
data.data = {};
- if (request.isDataBinary){
+ if (request.isDataBinary) {
+ let filePath = '';
- let filePath = ''
-
- if(request.data.startsWith('@')){
+ if (request.data.startsWith('@')) {
filePath = request.data.slice(1);
- }else{
+ } else {
filePath = request.data;
}
- const fileName = path.basename(filePath);
-
data.data = [
{
- name: repr(fileName),
- value: [repr(filePath)],
- enabled: true,
+ filePath: repr(filePath),
contentType: request.headers['Content-Type'],
- type: 'binaryFile'
+ selected: true,
}
];
return data;
-
}
data.files = {};
@@ -190,13 +184,11 @@ const curlToJson = (curlCommand) => {
if (request.query) {
requestJson.queries = getQueries(request);
- }
-
- else if (request.multipartUploads || request.isDataBinary) {
+ } else if (request.multipartUploads || request.isDataBinary) {
Object.assign(requestJson, getFilesString(request));
} else if (typeof request.data === 'string' || typeof request.data === 'number') {
Object.assign(requestJson, getDataString(request));
- }
+ }
if (request.insecure) {
requestJson.insecure = false;
diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js
index c0391f4d6..5285ebd9b 100644
--- a/packages/bruno-electron/src/ipc/collection.js
+++ b/packages/bruno-electron/src/ipc/collection.js
@@ -54,14 +54,11 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
});
// browse directory for file
- ipcMain.handle('renderer:browse-files', async (event, pathname, request, filters, properties) => {
+ ipcMain.handle('renderer:browse-files', async (_, filters, properties) => {
try {
-
- const filePaths = await browseFiles(mainWindow, filters, properties);
-
- return filePaths;
+ return await browseFiles(mainWindow, filters, properties);
} catch (error) {
- return Promise.reject(error);
+ throw error;
}
});
diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js
index 7495c94fa..7e06fd23c 100644
--- a/packages/bruno-electron/src/ipc/network/index.js
+++ b/packages/bruno-electron/src/ipc/network/index.js
@@ -583,7 +583,6 @@ const registerNetworkIpc = (mainWindow) => {
try {
request.signal = abortController.signal;
-
saveCancelToken(cancelTokenUid, abortController);
await runPreRequest(
@@ -614,7 +613,7 @@ const registerNetworkIpc = (mainWindow) => {
url: request.url,
method: request.method,
headers: request.headers,
- data: request.mode == 'binaryFile'? undefined: safeParseJSON(safeStringifyJSON(request.data)) ,
+ data: request.mode == 'binaryFile'? "": safeParseJSON(safeStringifyJSON(request.data)) ,
timestamp: Date.now()
},
collectionUid,
diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js
index 297033c6d..70d8017d1 100644
--- a/packages/bruno-electron/src/ipc/network/prepare-request.js
+++ b/packages/bruno-electron/src/ipc/network/prepare-request.js
@@ -1,4 +1,4 @@
-const { get, each, filter } = require('lodash');
+const { get, each, filter, find } = require('lodash');
const decomment = require('decomment');
const crypto = require('node:crypto');
const fs = require('node:fs/promises');
@@ -254,30 +254,25 @@ const prepareRequest = async (item, collection, abortController) => {
}
if (request.body.mode === 'binaryFile') {
-
if (!contentTypeDefined) {
- axiosRequest.headers['content-type'] = 'application/octet-stream';
+ axiosRequest.headers['content-type'] = 'application/octet-stream'; // Default headers for binary file uploads
}
-
- if (request.body.binaryFile && request.body.binaryFile.length > 0) {
-
- axiosRequest.headers['content-type'] = request.body.binaryFile[0].contentType;
-
- let filePath = request.body.binaryFile[0].value[0];
-
- if (filePath && filePath !== '') {
-
+
+ const binaryFile = find(request.body.binaryFile, (param) => param.selected);
+ if (binaryFile) {
+ let { filePath, contentType } = binaryFile;
+
+ axiosRequest.headers['content-type'] = contentType;
+ if (filePath) {
if (!path.isAbsolute(filePath)) {
-
filePath = path.join(collectionPath, filePath);
}
-
- const file = await fs.readFile(filePath, abortController)
-
- axiosRequest.data = file
-
- if(axiosRequest.headers['content-type'].includes('application/json')) {
- axiosRequest.data = JSON.parse(file)
+
+ try {
+ const fileContent = await fs.readFile(filePath);
+ axiosRequest.data = fileContent;
+ } catch (error) {
+ console.error('Error reading file:', error);
}
}
}
diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js
index e7b0c5772..3b228036f 100644
--- a/packages/bruno-lang/v2/src/bruToJson.js
+++ b/packages/bruno-lang/v2/src/bruToJson.js
@@ -177,9 +177,9 @@ const multipartExtractContentType = (pair) => {
const binaryFileExtractContentType = (pair) => {
if (_.isString(pair.value)) {
const match = pair.value.match(/^(.*?)\s*@contentType\((.*?)\)\s*$/);
- if (match != null && match.length > 2) {
- pair.value = match[1];
- pair.contentType = match[2];
+ if (match && match.length > 2) {
+ pair.value = match[1].trim();
+ pair.contentType = match[2].trim();
} else {
pair.contentType = '';
}
@@ -206,21 +206,25 @@ const mapPairListToKeyValPairsMultipart = (pairList = [], parseEnabled = true) =
const mapPairListToKeyValPairsBinaryFile = (pairList = [], parseEnabled = true) => {
const pairs = mapPairListToKeyValPairs(pairList, parseEnabled);
-
return pairs.map((pair) => {
binaryFileExtractContentType(pair);
if (pair.value.startsWith('@file(') && pair.value.endsWith(')')) {
- let filestr = pair.value.replace(/^@file\(/, '').replace(/\)$/, '');
- pair.type = 'binaryFile';
- pair.value = filestr != '' ? filestr.split('|') : [''];
+ let filePath = pair.value.replace(/^@file\(/, '').replace(/\)$/, '');
+ pair.filePath = filePath;
+ pair.selected = pair.enabled
+
+ // Remove pair.value as it only contains the file path reference
+ delete pair.value;
+ // Remove pair.name as it is auto-generated (e.g., file1, file2, file3, etc.)
+ delete pair.name;
+ delete pair.enabled;
}
return pair;
});
};
-
const concatArrays = (objValue, srcValue) => {
if (_.isArray(objValue) && _.isArray(srcValue)) {
return objValue.concat(srcValue);
@@ -746,3 +750,4 @@ const parser = (input) => {
};
module.exports = parser;
+
\ No newline at end of file
diff --git a/packages/bruno-lang/v2/src/jsonToBru.js b/packages/bruno-lang/v2/src/jsonToBru.js
index c4e2ba323..164ea6a35 100644
--- a/packages/bruno-lang/v2/src/jsonToBru.js
+++ b/packages/bruno-lang/v2/src/jsonToBru.js
@@ -2,8 +2,8 @@ const _ = require('lodash');
const { indentString } = require('../../v1/src/utils');
-const enabled = (items = []) => items.filter((item) => item.enabled);
-const disabled = (items = []) => items.filter((item) => !item.enabled);
+const enabled = (items = [], key = "enabled") => items.filter((item) => item[key]);
+const disabled = (items = [], key = "enabled") => items.filter((item) => !item[key]);
// remove the last line if two new lines are found
const stripLastLine = (text) => {
@@ -316,21 +316,19 @@ ${indentString(body.sparql)}
if (body && body.binaryFile && body.binaryFile.length) {
bru += `body:binary-file {`;
- const binaryFiles = enabled(body.binaryFile).concat(disabled(body.binaryFile));
+ const binaryFiles = enabled(body.binaryFile, "selected").concat(disabled(body.binaryFile, "selected"));
if (binaryFiles.length) {
bru += `\n${indentString(
binaryFiles
.map((item) => {
- const enabled = item.enabled ? '' : '~';
+ const selected = item.selected ? '' : '~';
const contentType =
item.contentType && item.contentType !== '' ? ' @contentType(' + item.contentType + ')' : '';
-
- if (item.type === 'binaryFile') {
- let filestr = item.value[0] || '';
- const value = `@file(${filestr})`;
- return `${enabled}${item.name}: ${value}${contentType}`;
- }
+ const filePath = item.filePath || '';
+ const value = `@file(${filePath})`;
+ const itemName = "file";
+ return `${selected}${itemName}: ${value}${contentType}`;
})
.join('\n')
)}`;
diff --git a/packages/bruno-lang/v2/tests/fixtures/request.bru b/packages/bruno-lang/v2/tests/fixtures/request.bru
index 5f7183f34..59f37ac89 100644
--- a/packages/bruno-lang/v2/tests/fixtures/request.bru
+++ b/packages/bruno-lang/v2/tests/fixtures/request.bru
@@ -104,7 +104,8 @@ body:multipart-form {
body:binary-file {
file: @file(path/to/file.json) @contentType(application/json)
- ~file2: @file(path/to/file2.json) @contentType(application/json)
+ file: @file(path/to/file.json) @contentType(application/json)
+ ~file: @file(path/to/file2.json) @contentType(application/json)
}
body:graphql {
diff --git a/packages/bruno-lang/v2/tests/fixtures/request.json b/packages/bruno-lang/v2/tests/fixtures/request.json
index ad7a45495..166040509 100644
--- a/packages/bruno-lang/v2/tests/fixtures/request.json
+++ b/packages/bruno-lang/v2/tests/fixtures/request.json
@@ -140,18 +140,19 @@
],
"binaryFile" : [
{
- "name": "file",
- "value": ["path/to/file.json"],
- "enabled": true,
- "type": "binaryFile",
- "contentType": "application/json"
+ "filePath": "path/to/file.json",
+ "contentType": "application/json",
+ "selected": true
},
{
- "name": "file2",
- "value": ["path/to/file2.json"],
- "enabled": false,
- "type": "binaryFile",
- "contentType": "application/json"
+ "filePath": "path/to/file.json",
+ "contentType": "application/json",
+ "selected": true
+ },
+ {
+ "filePath": "path/to/file2.json",
+ "contentType": "application/json",
+ "selected": false
}
]
},
diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js
index 19d98afbb..fdf88b38c 100644
--- a/packages/bruno-schema/src/collections/index.js
+++ b/packages/bruno-schema/src/collections/index.js
@@ -77,11 +77,9 @@ const multipartFormSchema = Yup.object({
const binaryFileSchema = Yup.object({
uid: uidSchema,
- type: Yup.string().oneOf(['binaryFile']).required('type is required'),
- name: Yup.string().nullable(),
- value: Yup.array().of(Yup.string().nullable()).nullable(),
+ filePath: Yup.string().nullable(),
contentType: Yup.string().nullable(),
- enabled: Yup.boolean()
+ selected: Yup.boolean()
})
.noUnknown(true)
.strict();
diff --git a/packages/bruno-tests/collection/echo/multiline/echo binary.bru b/packages/bruno-tests/collection/echo/multiline/echo binary.bru
new file mode 100644
index 000000000..d11b30413
--- /dev/null
+++ b/packages/bruno-tests/collection/echo/multiline/echo binary.bru
@@ -0,0 +1,15 @@
+meta {
+ name: echo binary
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{echo-host}}
+ body: binaryFile
+ auth: none
+}
+
+body:binary-file {
+ file: @file(bruno.png) @contentType(image/png)
+}
diff --git a/packages/bruno-tests/src/echo/index.js b/packages/bruno-tests/src/echo/index.js
index ba9b403ae..89a9208e0 100644
--- a/packages/bruno-tests/src/echo/index.js
+++ b/packages/bruno-tests/src/echo/index.js
@@ -19,6 +19,17 @@ router.post('/xml-raw', (req, res) => {
return res.send(req.rawBody);
});
+router.post('/bin', (req, res) => {
+ const rawBody = req.body;
+
+ if (!rawBody || rawBody.length === 0) {
+ return res.status(400).send('No data received');
+ }
+
+ res.set('Content-Type', req.headers['content-type'] || 'application/octet-stream');
+ res.send(rawBody);
+});
+
router.get('/bom-json-test', (req, res) => {
const jsonData = {
message: 'Hello!',
diff --git a/packages/bruno-tests/src/index.js b/packages/bruno-tests/src/index.js
index a09cb434b..a482fc128 100644
--- a/packages/bruno-tests/src/index.js
+++ b/packages/bruno-tests/src/index.js
@@ -10,6 +10,7 @@ const multipartRouter = require('./multipart');
const app = new express();
const port = process.env.PORT || 8080;
+app.use(express.raw({type: '*/*', limit: '100mb'}));
app.use(cors());
app.use(xmlParser());
app.use(bodyParser.text());