diff --git a/package-lock.json b/package-lock.json index 2cfe22abb..f53c786a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24043,7 +24043,7 @@ "graphiql": "3.7.1", "graphql": "^16.6.0", "graphql-request": "^3.7.0", - "httpsnippet": "^3.0.9", + "httpsnippet": "^3.0.6", "i18next": "24.1.2", "idb": "^7.0.0", "immer": "^9.0.15", diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index 7203bb816..c824f13a8 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -33,7 +33,7 @@ "graphiql": "3.7.1", "graphql": "^16.6.0", "graphql-request": "^3.7.0", - "httpsnippet": "^3.0.9", + "httpsnippet": "^3.0.6", "i18next": "24.1.2", "idb": "^7.0.0", "immer": "^9.0.15", diff --git a/packages/bruno-app/src/components/FilePickerEditor/index.js b/packages/bruno-app/src/components/FilePickerEditor/index.js index 9387800d2..d976a3e79 100644 --- a/packages/bruno-app/src/components/FilePickerEditor/index.js +++ b/packages/bruno-app/src/components/FilePickerEditor/index.js @@ -6,10 +6,10 @@ import { IconX } from '@tabler/icons'; import { isWindowsOS } from 'utils/common/platform'; import slash from 'utils/common/slash'; -const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = false }) => { +const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = false}) => { value = value || []; const dispatch = useDispatch(); - const filenames = (isSingleFilePicker ? [value] : value || []) + const filenames = value .filter((v) => v != null && v != '') .map((v) => { const separator = isWindowsOS() ? '\\' : '/'; @@ -20,7 +20,7 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa const title = filenames.map((v) => `- ${v}`).join('\n'); const browse = () => { - dispatch(browseFiles([], [!isSingleFilePicker ? "multiSelections": ""])) + dispatch(browseFiles([],[''])) .then((filePaths) => { // If file is in the collection's directory, then we use relative path // Otherwise, we use the absolute path @@ -34,7 +34,7 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa return filePath; }); - onChange(isSingleFilePicker ? filePaths[0] : filePaths); + onChange(filePaths); }) .catch((error) => { console.error(error); @@ -42,7 +42,7 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa }; const clear = () => { - onChange(isSingleFilePicker ? '' : []); + onChange([]); }; const renderButtonText = (filenames) => { @@ -66,9 +66,9 @@ const FilePickerEditor = ({ value, onChange, collection, isSingleFilePicker = fa ) : ( ); }; -export default FilePickerEditor; \ No newline at end of file +export default FilePickerEditor; diff --git a/packages/bruno-app/src/components/RequestPane/FileBody/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/FileBody/StyledWrapper.js deleted file mode 100644 index 35adfcc1f..000000000 --- a/packages/bruno-app/src/components/RequestPane/FileBody/StyledWrapper.js +++ /dev/null @@ -1,65 +0,0 @@ -import styled from 'styled-components'; - -const Wrapper = styled.div` - table { - width: 100%; - border-collapse: collapse; - font-weight: 600; - table-layout: fixed; - - thead, - td { - border: 1px solid ${(props) => props.theme.table.border}; - } - - thead { - color: ${(props) => props.theme.table.thead.color}; - font-size: 0.8125rem; - user-select: none; - } - td { - padding: 6px 10px; - - &:nth-child(1) { - width: 30%; - } - - &:nth-child(2) { - width: 45%; - } - - &:nth-child(3) { - width: 25%; - } - - &:nth-child(4) { - width: 70px; - } - } - } - - .btn-add-param { - font-size: 0.8125rem; - } - - input[type='text'] { - width: 100%; - border: solid 1px transparent; - outline: none !important; - color: ${(props) => props.theme.table.input.color}; - background: transparent; - - &:focus { - outline: none !important; - border: solid 1px transparent; - } - } - - input[type='radio'] { - cursor: pointer; - position: relative; - top: 1px; - } -`; - -export default Wrapper; diff --git a/packages/bruno-app/src/components/RequestPane/FileBody/index.js b/packages/bruno-app/src/components/RequestPane/FileBody/index.js deleted file mode 100644 index ae1646330..000000000 --- a/packages/bruno-app/src/components/RequestPane/FileBody/index.js +++ /dev/null @@ -1,164 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { get, cloneDeep, isArray } from 'lodash'; -import { IconTrash } from '@tabler/icons'; -import { useDispatch } from 'react-redux'; -import { useTheme } from 'providers/Theme'; -import { addFileParam, updateFileParam, deleteFileParam } from 'providers/ReduxStore/slices/collections/index'; -import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; -import StyledWrapper from './StyledWrapper'; -import FilePickerEditor from 'components/FilePickerEditor/index'; -import SingleLineEditor from 'components/SingleLineEditor/index'; - -const FileBody = ({ item, collection }) => { - const dispatch = useDispatch(); - const { storedTheme } = useTheme(); - const params = item.draft ? get(item, 'draft.request.body.file') : get(item, 'request.body.file'); - - const [enabledFileUid, setEnableFileUid] = useState(params && params.length ? params[0].uid : ''); - - const addFile = () => { - dispatch( - addFileParam({ - itemUid: item.uid, - collectionUid: collection.uid, - }) - ); - }; - - const onSave = () => dispatch(saveRequest(item.uid, collection.uid)); - const handleRun = () => dispatch(sendRequest(item, collection.uid)); - - const handleParamChange = (e, _param, type) => { - const param = cloneDeep(_param); - switch (type) { - case 'filePath': { - param.filePath = e.target.filePath; - param.contentType = ""; - break; - } - case 'contentType': { - param.contentType = e.target.contentType; - break; - } - case 'selected': { - param.selected = e.target.selected; - setEnableFileUid(param.uid) - break; - } - } - dispatch( - updateFileParam({ - param: param, - itemUid: item.uid, - collectionUid: collection.uid - }) - ); - }; - - const handleRemoveParams = (param) => { - dispatch( - deleteFileParam({ - paramUid: param.uid, - itemUid: item.uid, - collectionUid: collection.uid - }) - ); - }; - - return ( - - - - - - - - - - - - {params && params.length - ? params.map((param, index) => { - return ( - - - - - - - ); - }) - : null} - -
-
File
-
-
Content-Type
-
-
Selected
-
- - handleParamChange( - { - target: { - filePath: path - } - }, - param, - 'filePath' - ) - } - collection={collection} - /> - - - handleParamChange( - { - target: { - contentType: newValue - } - }, - param, - 'contentType' - ) - } - onRun={handleRun} - collection={collection} - /> - -
- handleParamChange(e, param, 'selected')} - /> -
-
-
- -
-
-
- -
-
- ); -}; -export default FileBody; \ No newline at end of file diff --git a/packages/bruno-app/src/components/RequestPane/RequestBody/RequestBodyMode/index.js b/packages/bruno-app/src/components/RequestPane/RequestBody/RequestBodyMode/index.js index db73597df..95b3b6a55 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestBody/RequestBodyMode/index.js +++ b/packages/bruno-app/src/components/RequestPane/RequestBody/RequestBodyMode/index.js @@ -132,10 +132,10 @@ const RequestBodyMode = ({ item, collection }) => { className="dropdown-item" onClick={() => { dropdownTippyRef.current.hide(); - onModeChange('file'); + onModeChange('binaryFile'); }} > - File / Binary + Binary File
{ const dispatch = useDispatch(); @@ -63,8 +63,8 @@ const RequestBody = ({ item, collection }) => { ); } - if (bodyMode === 'file') { - return + if (bodyMode === 'binaryFile') { + return } if (bodyMode === 'formUrlEncoded') { 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 d251112c7..f089dbddc 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -759,7 +759,7 @@ export const newHttpRequest = (params) => (dispatch, getState) => { sparql: null, multipartForm: null, formUrlEncoded: null, - file: null + binaryFile: null }, auth: auth ?? { mode: 'none' @@ -1040,16 +1040,13 @@ 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', filters, properties) - .then(resolve) - .catch(reject); + ipcRenderer.invoke('renderer:browse-files', undefined, undefined, undefined, 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 dcf130065..bdecc1543 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -961,76 +961,6 @@ export const collectionsSlice = createSlice({ } } }, - addFileParam: (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.file = item.draft.request.body.file || []; - - item.draft.request.body.file.push({ - uid: uuid(), - filePath: '', - contentType: '', - selected: false - }); - } - } - }, - updateFileParam: (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); - } - - const param = find(item.draft.request.body.file, (p) => p.uid === action.payload.param.uid); - - if (param) { - const contentType = mime.contentType(path.extname(action.payload.param.filePath)); - param.filePath = action.payload.param.filePath; - param.contentType = action.payload.param.contentType || contentType || ''; - param.selected = action.payload.param.selected; - - item.draft.request.body.file = item.draft.request.body.file.map((p) => { - p.selected = p.uid === param.uid; - return p; - }); - } - } - } - }, - deleteFileParam: (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.file = filter( - item.draft.request.body.file, - (p) => p.uid !== action.payload.paramUid - ); - - if (item.draft.request.body.file.length > 0) { - item.draft.request.body.file[0].selected = true; - } - } - } - }, updateRequestAuthMode: (state, action) => { const collection = findCollectionByUid(state.collections, action.payload.collectionUid); @@ -1087,8 +1017,8 @@ export const collectionsSlice = createSlice({ item.draft.request.body.sparql = action.payload.content; break; } - case 'file': { - item.draft.request.body.file = action.payload.content; + case 'binaryFile': { + item.draft.request.body.binaryFile = action.payload.content; break; } case 'formUrlEncoded': { @@ -2092,9 +2022,9 @@ export const { addMultipartFormParam, updateMultipartFormParam, deleteMultipartFormParam, - addFileParam, - updateFileParam, - deleteFileParam, + addBinaryFile, + updateBinaryFile, + deleteBinaryFile, moveMultipartFormParam, updateRequestAuthMode, updateRequestBodyMode, diff --git a/packages/bruno-app/src/utils/codegenerator/har.js b/packages/bruno-app/src/utils/codegenerator/har.js index a4128b0c0..19e4ea4de 100644 --- a/packages/bruno-app/src/utils/codegenerator/har.js +++ b/packages/bruno-app/src/utils/codegenerator/har.js @@ -14,7 +14,7 @@ const createContentType = (mode) => { return 'application/json'; case 'multipartForm': return 'multipart/form-data'; - case 'file': + case 'binaryFile': return 'application/octet-stream'; default: return ''; @@ -65,23 +65,6 @@ 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, @@ -93,15 +76,20 @@ const createPostData = (body, type) => { ...(param.type === 'file' && { fileName: param.value }) })) }; - case 'file': + case 'binaryFile': const binary = { - mimeType: body[body.mode].filter((param) => param.enabled)[0].contentType, + mimeType: 'application/octet-stream', + // mimeType: body[body.mode].filter((param) => param.enabled)[0].contentType, params: body[body.mode] - .filter((param) => param.selected) + .filter((param) => param.enabled) .map((param) => ({ - value: param.filePath, + name: param.name, + value: param.value, + fileName: param.value })) }; + + console.log('curl-binary', binary); return binary; default: return { diff --git a/packages/bruno-app/src/utils/collections/export.js b/packages/bruno-app/src/utils/collections/export.js index 3d15fdd07..b7ca9f5ba 100644 --- a/packages/bruno-app/src/utils/collections/export.js +++ b/packages/bruno-app/src/utils/collections/export.js @@ -14,7 +14,7 @@ export const deleteUidsInItems = (items) => { each(get(item, 'request.vars.assertions'), (a) => delete a.uid); each(get(item, 'request.body.multipartForm'), (param) => delete param.uid); each(get(item, 'request.body.formUrlEncoded'), (param) => delete param.uid); - each(get(item, 'request.body.file'), (param) => delete param.uid); + each(get(item, 'request.body.binaryFile'), (param) => delete param.uid); } if (item.items && item.items.length) { diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index eb53cfb48..309c245eb 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -281,13 +281,15 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} }); }; - const copyFileParams = (params = []) => { + const copyBinaryFileParams = (params = []) => { return map(params, (param) => { return { uid: param.uid, - filePath: param.filePath, + type: param.type, + name: param.name, + value: param.value, contentType: param.contentType, - selected: param.selected + enabled: param.enabled } }); } @@ -320,7 +322,7 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} sparql: si.request.body.sparql, formUrlEncoded: copyFormUrlEncodedParams(si.request.body.formUrlEncoded), multipartForm: copyMultipartFormParams(si.request.body.multipartForm), - file: copyFileParams(si.request.body.file) + binaryFile: copyBinaryFileParams(si.request.body.binaryFile) }, script: si.request.script, vars: si.request.vars, @@ -673,8 +675,8 @@ export const humanizeRequestBodyMode = (mode) => { label = 'SPARQL'; break; } - case 'file': { - label = 'File / Binary'; + case 'binaryFile': { + label = 'Binary File'; break; } case 'formUrlEncoded': { @@ -777,7 +779,7 @@ export const refreshUidsInItem = (item) => { each(get(item, 'request.params'), (param) => (param.uid = uuid())); each(get(item, 'request.body.multipartForm'), (param) => (param.uid = uuid())); each(get(item, 'request.body.formUrlEncoded'), (param) => (param.uid = uuid())); - each(get(item, 'request.body.file'), (param) => (param.uid = uuid())); + each(get(item, 'request.body.binaryFile'), (param) => (param.uid = uuid())); return item; }; @@ -788,13 +790,13 @@ export const deleteUidsInItem = (item) => { const headers = get(item, 'request.headers', []); const bodyFormUrlEncoded = get(item, 'request.body.formUrlEncoded', []); const bodyMultipartForm = get(item, 'request.body.multipartForm', []); - const file = get(item, 'request.body.file', []); + const binaryFile = get(item, 'request.body.binaryFile', []); params.forEach((param) => delete param.uid); headers.forEach((header) => delete header.uid); bodyFormUrlEncoded.forEach((param) => delete param.uid); bodyMultipartForm.forEach((param) => delete param.uid); - file.forEach((param) => delete param.uid); + binaryFile.forEach((param) => delete param.uid); return item; }; 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 69305b370..5a2c62022 100644 --- a/packages/bruno-app/src/utils/curl/curl-to-json.js +++ b/packages/bruno-app/src/utils/curl/curl-to-json.js @@ -101,24 +101,31 @@ function getFilesString(request) { const data = {}; data.data = {}; - if (request.isDataBinary) { - let filePath = ''; - if (request.data.startsWith('@')) { + if (request.isDataBinary){ + + let filePath = '' + + if(request.data.startsWith('@')){ filePath = request.data.slice(1); - } else { + }else{ filePath = request.data; } + const fileName = path.basename(filePath); + data.data = [ { - filePath: repr(filePath), + name: repr(fileName), + value: [repr(filePath)], + enabled: true, contentType: request.headers['Content-Type'], - selected: true, + type: 'binaryFile' } ]; return data; + } data.files = {}; @@ -183,11 +190,13 @@ 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-app/src/utils/curl/curl-to-json.spec.js b/packages/bruno-app/src/utils/curl/curl-to-json.spec.js index 4c3194c2e..6f8206139 100644 --- a/packages/bruno-app/src/utils/curl/curl-to-json.spec.js +++ b/packages/bruno-app/src/utils/curl/curl-to-json.spec.js @@ -117,7 +117,7 @@ describe('curlToJson', () => { value: ['/path/to/file'], enabled: true, contentType: 'application/json;charset=utf-8', - type: 'file' + type: 'binaryFile' } ] }); diff --git a/packages/bruno-app/src/utils/curl/index.js b/packages/bruno-app/src/utils/curl/index.js index ad4f1edf6..d91588178 100644 --- a/packages/bruno-app/src/utils/curl/index.js +++ b/packages/bruno-app/src/utils/curl/index.js @@ -51,7 +51,7 @@ export const getRequestFromCurlCommand = (curlCommand, requestType = 'http-reque multipartForm: null, formUrlEncoded: null, graphql: null, - file: null + binaryFile: null }; if (parsedBody && contentType && typeof contentType === 'string') { @@ -59,8 +59,8 @@ export const getRequestFromCurlCommand = (curlCommand, requestType = 'http-reque body.mode = 'graphql'; body.graphql = parseGraphQL(parsedBody); } else if (requestType === 'http-request' && request.isDataBinary) { - body.mode = 'file'; - body.file = parsedBody; + body.mode = 'binaryFile'; + body.binaryFile = parsedBody; }else if (contentType.includes('application/json')) { body.mode = 'json'; body.json = convertToCodeMirrorJson(parsedBody); diff --git a/packages/bruno-app/src/utils/importers/common.js b/packages/bruno-app/src/utils/importers/common.js index 9d370a455..af187cc82 100644 --- a/packages/bruno-app/src/utils/importers/common.js +++ b/packages/bruno-app/src/utils/importers/common.js @@ -35,7 +35,7 @@ export const updateUidsInCollection = (_collection) => { each(get(item, 'request.assertions'), (a) => (a.uid = uuid())); each(get(item, 'request.body.multipartForm'), (param) => (param.uid = uuid())); each(get(item, 'request.body.formUrlEncoded'), (param) => (param.uid = uuid())); - each(get(item, 'request.body.file'), (param) => (param.uid = uuid())); + each(get(item, 'request.body.binaryFile'), (param) => (param.uid = uuid())); if (item.items && item.items.length) { updateItemUids(item.items); diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index cb73d2dc6..cdf488e75 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -62,11 +62,14 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection }); // browse directory for file - ipcMain.handle('renderer:browse-files', async (_, filters, properties) => { + ipcMain.handle('renderer:browse-files', async (event, pathname, request, filters, properties) => { try { - return await browseFiles(mainWindow, filters, properties); + + const filePaths = await browseFiles(mainWindow, filters, properties); + + return filePaths; } catch (error) { - throw error; + return Promise.reject(error); } }); diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index b76113e42..dbc36d846 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -28,7 +28,7 @@ const { makeAxiosInstance } = require('./axios-instance'); const { addAwsV4Interceptor, resolveAwsV4Credentials } = require('./awsv4auth-helper'); const { addDigestInterceptor } = require('./digestauth-helper'); const { shouldUseProxy, PatchedHttpsProxyAgent } = require('../../utils/proxy-util'); -const { chooseFileToSave, writeFile } = require('../../utils/filesystem'); +const { chooseFileToSave, writeBinaryFile, writeFile } = require('../../utils/filesystem'); const { getCookieStringForUrl, addCookieToJar, getDomainsWithCookies } = require('../../utils/cookies'); const { resolveOAuth2AuthorizationCodeAccessToken, @@ -587,6 +587,7 @@ const registerNetworkIpc = (mainWindow) => { try { request.signal = abortController.signal; + saveCancelToken(cancelTokenUid, abortController); await runPreRequest( @@ -617,7 +618,7 @@ const registerNetworkIpc = (mainWindow) => { url: request.url, method: request.method, headers: request.headers, - data: request.mode == 'file'? "": safeParseJSON(safeStringifyJSON(request.data)), + data: request.mode == 'binaryFile'? undefined: safeParseJSON(safeStringifyJSON(request.data)) , timestamp: Date.now() }, collectionUid, @@ -1374,7 +1375,7 @@ const registerNetworkIpc = (mainWindow) => { if (encoding === 'utf-8') { await writeFile(filePath, data); } else { - await writeFile(filePath, data); + await writeBinaryFile(filePath, data); } } } catch (error) { diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js index 137b74440..297033c6d 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, find } = require('lodash'); +const { get, each, filter } = require('lodash'); const decomment = require('decomment'); const crypto = require('node:crypto'); const fs = require('node:fs/promises'); @@ -253,26 +253,31 @@ const prepareRequest = async (item, collection, abortController) => { axiosRequest.data = request.body.sparql; } - if (request.body.mode === 'file') { + if (request.body.mode === 'binaryFile') { + if (!contentTypeDefined) { - axiosRequest.headers['content-type'] = 'application/octet-stream'; // Default headers for binary file uploads + axiosRequest.headers['content-type'] = 'application/octet-stream'; } - - const file = find(request.body.file, (param) => param.selected); - if (file) { - let { filePath, contentType } = file; - - axiosRequest.headers['content-type'] = contentType; - if (filePath) { + + 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 !== '') { + if (!path.isAbsolute(filePath)) { + filePath = path.join(collectionPath, filePath); } - - try { - const fileContent = await fs.readFile(filePath); - axiosRequest.data = fileContent; - } catch (error) { - console.error('Error reading file:', error); + + const file = await fs.readFile(filePath, abortController) + + axiosRequest.data = file + + if(axiosRequest.headers['content-type'].includes('application/json')) { + axiosRequest.data = JSON.parse(file) } } } diff --git a/packages/bruno-electron/src/utils/filesystem.js b/packages/bruno-electron/src/utils/filesystem.js index 4bf4c9970..d37742be4 100644 --- a/packages/bruno-electron/src/utils/filesystem.js +++ b/packages/bruno-electron/src/utils/filesystem.js @@ -69,6 +69,16 @@ function normalizeWslPath(pathname) { } const writeFile = async (pathname, content) => { + try { + fs.writeFileSync(pathname, content, { + encoding: 'utf8' + }); + } catch (err) { + return Promise.reject(err); + } +}; + +const writeBinaryFile = async (pathname, content) => { try { fs.writeFileSync(pathname, content); } catch (err) { @@ -255,6 +265,7 @@ module.exports = { isWSLPath, normalizeWslPath, writeFile, + writeBinaryFile, hasJsonExtension, hasBruExtension, createDirectory, diff --git a/packages/bruno-lang/v2/src/bruToJson.js b/packages/bruno-lang/v2/src/bruToJson.js index 26a670681..e7b0c5772 100644 --- a/packages/bruno-lang/v2/src/bruToJson.js +++ b/packages/bruno-lang/v2/src/bruToJson.js @@ -25,7 +25,7 @@ const grammar = ohm.grammar(`Bru { BruFile = (meta | http | query | params | headers | auths | bodies | varsandassert | script | tests | docs)* auths = authawsv4 | authbasic | authbearer | authdigest | authNTLM | authOAuth2 | authwsse | authapikey bodies = bodyjson | bodytext | bodyxml | bodysparql | bodygraphql | bodygraphqlvars | bodyforms | body - bodyforms = bodyformurlencoded | bodymultipart | bodyfile + bodyforms = bodyformurlencoded | bodymultipart | bodybinaryfile params = paramspath | paramsquery nl = "\\r"? "\\n" @@ -102,7 +102,7 @@ const grammar = ohm.grammar(`Bru { bodyformurlencoded = "body:form-urlencoded" dictionary bodymultipart = "body:multipart-form" dictionary - bodyfile = "body:file" dictionary + bodybinaryfile = "body:binary-file" dictionary script = scriptreq | scriptres scriptreq = "script:pre-request" st* "{" nl* textblock tagend @@ -174,12 +174,12 @@ const multipartExtractContentType = (pair) => { } }; -const fileExtractContentType = (pair) => { +const binaryFileExtractContentType = (pair) => { if (_.isString(pair.value)) { const match = pair.value.match(/^(.*?)\s*@contentType\((.*?)\)\s*$/); - if (match && match.length > 2) { - pair.value = match[1].trim(); - pair.contentType = match[2].trim(); + if (match != null && match.length > 2) { + pair.value = match[1]; + pair.contentType = match[2]; } else { pair.contentType = ''; } @@ -204,27 +204,23 @@ const mapPairListToKeyValPairsMultipart = (pairList = [], parseEnabled = true) = }); }; -const mapPairListToKeyValPairsFile = (pairList = [], parseEnabled = true) => { +const mapPairListToKeyValPairsBinaryFile = (pairList = [], parseEnabled = true) => { const pairs = mapPairListToKeyValPairs(pairList, parseEnabled); + return pairs.map((pair) => { - fileExtractContentType(pair); + binaryFileExtractContentType(pair); if (pair.value.startsWith('@file(') && pair.value.endsWith(')')) { - 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; + let filestr = pair.value.replace(/^@file\(/, '').replace(/\)$/, ''); + pair.type = 'binaryFile'; + pair.value = filestr != '' ? filestr.split('|') : ['']; } return pair; }); }; + const concatArrays = (objValue, srcValue) => { if (_.isArray(objValue) && _.isArray(srcValue)) { return objValue.concat(srcValue); @@ -609,10 +605,10 @@ const sem = grammar.createSemantics().addAttribute('ast', { } }; }, - bodyfile(_1, dictionary) { + bodybinaryfile(_1, dictionary) { return { body: { - file: mapPairListToKeyValPairsFile(dictionary.ast) + binaryFile: mapPairListToKeyValPairsBinaryFile(dictionary.ast) } }; }, @@ -750,4 +746,3 @@ 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 12aaa1894..c4e2ba323 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 = [], key = "enabled") => items.filter((item) => item[key]); -const disabled = (items = [], key = "enabled") => items.filter((item) => !item[key]); +const enabled = (items = []) => items.filter((item) => item.enabled); +const disabled = (items = []) => items.filter((item) => !item.enabled); // remove the last line if two new lines are found const stripLastLine = (text) => { @@ -313,21 +313,24 @@ ${indentString(body.sparql)} bru += '\n}\n\n'; } - if (body && body.file && body.file.length) { - bru += `body:file {`; - const files = enabled(body.file, "selected").concat(disabled(body.file, "selected")); - if (files.length) { + if (body && body.binaryFile && body.binaryFile.length) { + bru += `body:binary-file {`; + const binaryFiles = enabled(body.binaryFile).concat(disabled(body.binaryFile)); + + if (binaryFiles.length) { bru += `\n${indentString( - files + binaryFiles .map((item) => { - const selected = item.selected ? '' : '~'; + const enabled = item.enabled ? '' : '~'; const contentType = item.contentType && item.contentType !== '' ? ' @contentType(' + item.contentType + ')' : ''; - const filePath = item.filePath || ''; - const value = `@file(${filePath})`; - const itemName = "file"; - return `${selected}${itemName}: ${value}${contentType}`; + + if (item.type === 'binaryFile') { + let filestr = item.value[0] || ''; + const value = `@file(${filestr})`; + return `${enabled}${item.name}: ${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 ad66c64e8..5f7183f34 100644 --- a/packages/bruno-lang/v2/tests/fixtures/request.bru +++ b/packages/bruno-lang/v2/tests/fixtures/request.bru @@ -102,10 +102,9 @@ body:multipart-form { ~message: hello } -body:file { +body:binary-file { file: @file(path/to/file.json) @contentType(application/json) - file: @file(path/to/file.json) @contentType(application/json) - ~file: @file(path/to/file2.json) @contentType(application/json) + ~file2: @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 1cfe98809..ad7a45495 100644 --- a/packages/bruno-lang/v2/tests/fixtures/request.json +++ b/packages/bruno-lang/v2/tests/fixtures/request.json @@ -138,21 +138,20 @@ "type": "text" } ], - "file" : [ + "binaryFile" : [ { - "filePath": "path/to/file.json", - "contentType": "application/json", - "selected": true + "name": "file", + "value": ["path/to/file.json"], + "enabled": true, + "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 + "name": "file2", + "value": ["path/to/file2.json"], + "enabled": false, + "type": "binaryFile", + "contentType": "application/json" } ] }, diff --git a/packages/bruno-schema/src/collections/index.js b/packages/bruno-schema/src/collections/index.js index e0d05d167..19d98afbb 100644 --- a/packages/bruno-schema/src/collections/index.js +++ b/packages/bruno-schema/src/collections/index.js @@ -75,18 +75,20 @@ const multipartFormSchema = Yup.object({ .strict(); -const fileSchema = Yup.object({ +const binaryFileSchema = Yup.object({ uid: uidSchema, - filePath: Yup.string().nullable(), + type: Yup.string().oneOf(['binaryFile']).required('type is required'), + name: Yup.string().nullable(), + value: Yup.array().of(Yup.string().nullable()).nullable(), contentType: Yup.string().nullable(), - selected: Yup.boolean() + enabled: Yup.boolean() }) .noUnknown(true) .strict(); const requestBodySchema = Yup.object({ mode: Yup.string() - .oneOf(['none', 'json', 'text', 'xml', 'formUrlEncoded', 'multipartForm', 'graphql', 'sparql', 'file']) + .oneOf(['none', 'json', 'text', 'xml', 'formUrlEncoded', 'multipartForm', 'graphql', 'sparql', 'binaryFile']) .required('mode is required'), json: Yup.string().nullable(), text: Yup.string().nullable(), @@ -95,7 +97,7 @@ const requestBodySchema = Yup.object({ formUrlEncoded: Yup.array().of(keyValueSchema).nullable(), multipartForm: Yup.array().of(multipartFormSchema).nullable(), graphql: graphqlBodySchema.nullable(), - file: Yup.array().of(fileSchema).nullable() + binaryFile: Yup.array().of(binaryFileSchema).nullable() }) .noUnknown(true) .strict(); diff --git a/packages/bruno-tests/collection/echo/echo bom json.bru b/packages/bruno-tests/collection/echo/echo bom json.bru new file mode 100644 index 000000000..117c329ba --- /dev/null +++ b/packages/bruno-tests/collection/echo/echo bom json.bru @@ -0,0 +1,11 @@ +meta { + name: echo bom json + type: http + seq: 1 +} + +get { + url: {{host}}/api/echo/bom-json-test + body: none + auth: none +} \ No newline at end of file diff --git a/packages/bruno-tests/collection/echo/echo file body/bin.bru b/packages/bruno-tests/collection/echo/echo file body/bin.bru deleted file mode 100644 index fcb87d9c0..000000000 Binary files a/packages/bruno-tests/collection/echo/echo file body/bin.bru and /dev/null differ diff --git a/packages/bruno-tests/collection/echo/echo file body/bru.bru b/packages/bruno-tests/collection/echo/echo file body/bru.bru deleted file mode 100644 index 8fb60ef28..000000000 --- a/packages/bruno-tests/collection/echo/echo file body/bru.bru +++ /dev/null @@ -1,40 +0,0 @@ -meta { - name: bru - type: http - seq: 4 -} - -post { - url: {{echo-host}} - body: file - auth: none -} - -body:file { - file: @file(ping.bru) @contentType(bru) -} - -tests { - test("should return bru file body contents", function() { - const data = res.getBody(); - const expectedData = `meta { - name: ping - type: http - seq: 1 - } - - get { - url: {{host}}/ping - body: none - auth: none - } - `; - expect(res.getBody()).to.eql(expectedData); - }); - - - test("should return proper header", function() { - const contentType = res.getHeader('content-type'); - expect(contentType).to.eql('bru'); - }); -} diff --git a/packages/bruno-tests/collection/echo/echo file body/image.bru b/packages/bruno-tests/collection/echo/echo file body/image.bru deleted file mode 100644 index 01c5d408b..000000000 --- a/packages/bruno-tests/collection/echo/echo file body/image.bru +++ /dev/null @@ -1,22 +0,0 @@ -meta { - name: image - type: http - seq: 1 -} - -post { - url: {{echo-host}} - body: file - auth: none -} - -body:file { - file: @file(bruno.png) @contentType(image/png) -} - -tests { - test("should return proper header", function() { - const contentType = res.getHeader('content-type'); - expect(contentType).to.eql('image/png'); - }); -} diff --git a/packages/bruno-tests/collection/echo/echo file body/json.bru b/packages/bruno-tests/collection/echo/echo file body/json.bru deleted file mode 100644 index 42412038a..000000000 --- a/packages/bruno-tests/collection/echo/echo file body/json.bru +++ /dev/null @@ -1,55 +0,0 @@ -meta { - name: json - type: http - seq: 2 -} - -post { - url: {{echo-host}} - body: file - auth: none -} - -body:file { - file: @file(file.json) @contentType(application/json; charset=utf-8) -} - -tests { - test("should return json file body contents buffer", function() { - const data = res.getBody(); - expect(res.getBody()).to.eql({ - "type": "Buffer", - "data": [ - 123, - 10, - 32, - 32, - 34, - 104, - 101, - 108, - 108, - 111, - 34, - 58, - 32, - 34, - 98, - 114, - 117, - 110, - 111, - 34, - 10, - 125, - 10 - ] - }); - }); - - test("should return proper header", function() { - const contentType = res.getHeader('content-type'); - expect(contentType).to.eql('application/json; charset=utf-8'); - }); - -} diff --git a/packages/bruno-tests/collection/echo/echo file body/text.bru b/packages/bruno-tests/collection/echo/echo file body/text.bru deleted file mode 100644 index 663c150f5..000000000 --- a/packages/bruno-tests/collection/echo/echo file body/text.bru +++ /dev/null @@ -1,30 +0,0 @@ -meta { - name: text - type: http - seq: 3 -} - -post { - url: {{echo-host}} - body: file - auth: none -} - -body:file { - file: @file(file.txt) @contentType(text/plain; charset=utf-8) -} - -tests { - test("should return json file body contents", function() { - const data = res.getBody(); - const expectedData = `file.txt - - hello, bruno`; - expect(res.getBody()).to.eql(expectedData); - }); - - test("should return proper header", function() { - const contentType = res.getHeader('content-type'); - expect(contentType).to.eql('text/plain; charset=utf-8'); - }); -} diff --git a/packages/bruno-tests/collection/echo/echo json.bru b/packages/bruno-tests/collection/echo/echo json.bru index 0fa135fe8..1749eda6b 100644 --- a/packages/bruno-tests/collection/echo/echo json.bru +++ b/packages/bruno-tests/collection/echo/echo json.bru @@ -1,7 +1,7 @@ meta { name: echo json type: http - seq: 3 + seq: 2 } post { diff --git a/packages/bruno-tests/collection/echo/echo numbers.bru b/packages/bruno-tests/collection/echo/echo numbers.bru index 0d6126909..8f68fe558 100644 --- a/packages/bruno-tests/collection/echo/echo numbers.bru +++ b/packages/bruno-tests/collection/echo/echo numbers.bru @@ -1,7 +1,7 @@ meta { name: echo numbers type: http - seq: 2 + seq: 1 } post { diff --git a/packages/bruno-tests/collection/echo/echo plaintext.bru b/packages/bruno-tests/collection/echo/echo plaintext.bru index 3ace3bb2b..56a23d345 100644 --- a/packages/bruno-tests/collection/echo/echo plaintext.bru +++ b/packages/bruno-tests/collection/echo/echo plaintext.bru @@ -1,7 +1,7 @@ meta { name: echo plaintext type: http - seq: 4 + seq: 3 } post { diff --git a/packages/bruno-tests/collection/echo/echo xml parsed(self closing tags).bru b/packages/bruno-tests/collection/echo/echo xml parsed(self closing tags).bru index b378fb0c8..d337cebb3 100644 --- a/packages/bruno-tests/collection/echo/echo xml parsed(self closing tags).bru +++ b/packages/bruno-tests/collection/echo/echo xml parsed(self closing tags).bru @@ -1,7 +1,7 @@ meta { name: echo xml parsed(self closing tags) type: http - seq: 7 + seq: 6 } post { diff --git a/packages/bruno-tests/collection/echo/echo xml parsed.bru b/packages/bruno-tests/collection/echo/echo xml parsed.bru index 71363b71c..acd24a292 100644 --- a/packages/bruno-tests/collection/echo/echo xml parsed.bru +++ b/packages/bruno-tests/collection/echo/echo xml parsed.bru @@ -1,7 +1,7 @@ meta { name: echo xml parsed type: http - seq: 5 + seq: 4 } post { diff --git a/packages/bruno-tests/collection/echo/echo xml raw.bru b/packages/bruno-tests/collection/echo/echo xml raw.bru index 0f8bec255..6a02ac238 100644 --- a/packages/bruno-tests/collection/echo/echo xml raw.bru +++ b/packages/bruno-tests/collection/echo/echo xml raw.bru @@ -1,7 +1,7 @@ meta { name: echo xml raw type: http - seq: 6 + seq: 5 } post { diff --git a/packages/bruno-tests/collection/file.bin b/packages/bruno-tests/collection/file.bin deleted file mode 100644 index 8de62184f..000000000 Binary files a/packages/bruno-tests/collection/file.bin and /dev/null differ diff --git a/packages/bruno-tests/collection/file.txt b/packages/bruno-tests/collection/file.txt deleted file mode 100644 index 033b8756d..000000000 --- a/packages/bruno-tests/collection/file.txt +++ /dev/null @@ -1,3 +0,0 @@ -file.txt - -hello, bruno \ No newline at end of file diff --git a/packages/bruno-tests/src/echo/index.js b/packages/bruno-tests/src/echo/index.js index 89a9208e0..ba9b403ae 100644 --- a/packages/bruno-tests/src/echo/index.js +++ b/packages/bruno-tests/src/echo/index.js @@ -19,17 +19,6 @@ 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 a482fc128..a09cb434b 100644 --- a/packages/bruno-tests/src/index.js +++ b/packages/bruno-tests/src/index.js @@ -10,7 +10,6 @@ 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());