From acc8e9debab7e561b187bd49d2ee0895ead0efbf Mon Sep 17 00:00:00 2001 From: Pooja Date: Thu, 14 Aug 2025 15:31:29 +0530 Subject: [PATCH 1/8] Merge pull request #5327 from pooja-bruno/fix/cli-test-for-cookie fix: cli test for cookie --- .../collection/scripting/api/bru/cookies/getCookie.bru | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/bruno-tests/collection/scripting/api/bru/cookies/getCookie.bru b/packages/bruno-tests/collection/scripting/api/bru/cookies/getCookie.bru index 729592345..0e4a2bdf5 100644 --- a/packages/bruno-tests/collection/scripting/api/bru/cookies/getCookie.bru +++ b/packages/bruno-tests/collection/scripting/api/bru/cookies/getCookie.bru @@ -10,10 +10,16 @@ get { auth: inherit } +script:pre-request { + const jar = bru.cookies.jar() + + jar.setCookie("https://testbench-sanity.usebruno.com", "name", "value") +} + tests { const jar = bru.cookies.jar() - jar.getCookie("https://testbench-sanity.usebruno.com", "__cf_bm", function(error, data) { + jar.getCookie("https://testbench-sanity.usebruno.com", "name", function(error, data) { if(error) { console.error("Cookie retrieval error:", error) throw new Error(`Failed to get cookie: ${error.message || error}`) @@ -22,7 +28,7 @@ tests { test("should successfully retrieve cookie data", function() { expect(data).to.have.property('key'); expect(data).to.have.property('value'); - expect(data.key).to.equal("__cf_bm"); + expect(data.key).to.equal("name"); expect(data.value).to.be.a('string'); expect(data.value).to.not.be.empty; expect(data.domain).to.include('usebruno.com'); From c2063ce71bad0c5f37973e2efc00bbc820d10a64 Mon Sep 17 00:00:00 2001 From: Bijin A B Date: Thu, 14 Aug 2025 15:58:31 +0530 Subject: [PATCH 2/8] fix(security): patch CVE-2025-7783 by forcing form-data@4.0.4 (#5329) --- package-lock.json | 33 ++++++++++++++++++++++++++------- packages/bruno-app/package.json | 5 +++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index c2ae2ebca..fcef3b196 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13937,6 +13937,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -15411,13 +15426,15 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -16010,14 +16027,16 @@ } }, "node_modules/graphql-request/node_modules/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-sJe+TQb2vIaIyO783qN6BlMYWMw3WBOHA1Ay2qxsnjuafEOQFJ2JakedOQirT6D5XPRxDvS7AHYyem9fTpb4LQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index 5e0f6a8ea..aac3dbab7 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -111,5 +111,10 @@ "tailwindcss": "^3.4.1", "webpack": "^5.64.4", "webpack-cli": "^4.9.1" + }, + "overrides": { + "httpsnippet": { + "form-data": "4.0.4" + } } } From fc53dd88e215649e45e11cbd2e712030d9c96e01 Mon Sep 17 00:00:00 2001 From: sanish chirayath Date: Thu, 14 Aug 2025 20:47:17 +0530 Subject: [PATCH 3/8] fix: update authentication mode to inherit in OpenAPI to Bruno (#5300) --- .../src/openapi/openapi-to-bruno.js | 15 ++- .../openapi-circular-references.spec.js | 2 +- .../openapi-to-bruno/openapi-to-bruno.spec.js | 127 +++++++++++++++++- 3 files changed, 141 insertions(+), 3 deletions(-) diff --git a/packages/bruno-converters/src/openapi/openapi-to-bruno.js b/packages/bruno-converters/src/openapi/openapi-to-bruno.js index 0d177af30..7d1cf3b11 100644 --- a/packages/bruno-converters/src/openapi/openapi-to-bruno.js +++ b/packages/bruno-converters/src/openapi/openapi-to-bruno.js @@ -57,7 +57,7 @@ const transformOpenapiRequestItem = (request) => { url: ensureUrl(request.global.server + path), method: request.method.toUpperCase(), auth: { - mode: 'none', + mode: 'inherit', basic: null, bearer: null, digest: null @@ -419,6 +419,19 @@ export const parseOpenApiCollection = (data) => { uid: uuid(), name: group.name, type: 'folder', + root: { + request: { + auth: { + mode: 'inherit', + basic: null, + bearer: null, + digest: null + } + }, + meta: { + name: group.name + } + }, items: group.requests.map(transformOpenapiRequestItem) }; }); diff --git a/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-circular-references.spec.js b/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-circular-references.spec.js index eedf52567..debaedc72 100644 --- a/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-circular-references.spec.js +++ b/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-circular-references.spec.js @@ -233,7 +233,7 @@ const circularRefsOutput = { "url": "{{baseUrl}}/", "method": "POST", "auth": { - "mode": "none", + "mode": "inherit", }, "headers": [], "params": [], diff --git a/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-to-bruno.spec.js b/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-to-bruno.spec.js index 6cffe78f6..91ff66460 100644 --- a/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-to-bruno.spec.js +++ b/packages/bruno-converters/tests/openapi/openapi-to-bruno/openapi-to-bruno.spec.js @@ -8,6 +8,13 @@ describe('openapi-collection', () => { expect(brunoCollection).toMatchObject(expectedOutput); }); + it('should set auth mode to inherit when no security is defined in the collection', () => { + const brunoCollection = openApiToBruno(openApiCollectionString); + + // The openApiCollectionString has no security defined, so auth mode should be 'inherit' + expect(brunoCollection.items[0].items[0].request.auth.mode).toBe('inherit'); + }); + it('trims whitespace from info.title and uses the trimmed value as the collection name', () => { const openApiWithTitle = ` openapi: '3.0.0' @@ -109,6 +116,124 @@ servers: expect(result.name).toBe('Untitled Collection'); }); + describe('authentication inheritance', () => { + it('should set auth mode to inherit when no security is defined', () => { + const openApiWithoutSecurity = ` +openapi: '3.0.0' +info: + version: '1.0.0' + title: 'API without security' +paths: + /test: + get: + summary: 'Test endpoint' + operationId: 'testEndpoint' + responses: + '200': + description: 'OK' +servers: + - url: 'https://example.com' +`; + const result = openApiToBruno(openApiWithoutSecurity); + expect(result.items[0].request.auth.mode).toBe('inherit'); + }); + + it('should set auth mode to inherit when no global security schemes exist', () => { + const openApiWithEmptySecurity = ` +openapi: '3.0.0' +info: + version: '1.0.0' + title: 'API with empty security' +security: [] +paths: + /test: + get: + summary: 'Test endpoint' + operationId: 'testEndpoint' + responses: + '200': + description: 'OK' +servers: + - url: 'https://example.com' +`; + const result = openApiToBruno(openApiWithEmptySecurity); + expect(result.items[0].request.auth.mode).toBe('inherit'); + }); + + it('should set auth mode to inherit when components.securitySchemes is empty', () => { + const openApiWithEmptyComponents = ` +openapi: '3.0.0' +info: + version: '1.0.0' + title: 'API with empty components' +components: + securitySchemes: {} +paths: + /test: + get: + summary: 'Test endpoint' + operationId: 'testEndpoint' + responses: + '200': + description: 'OK' +servers: + - url: 'https://example.com' +`; + const result = openApiToBruno(openApiWithEmptyComponents); + expect(result.items[0].request.auth.mode).toBe('inherit'); + }); + + it('should set auth mode to inherit when operation has empty security array', () => { + const openApiWithEmptyOperationSecurity = ` +openapi: '3.0.0' +info: + version: '1.0.0' + title: 'API with empty operation security' +components: + securitySchemes: + basicAuth: + type: http + scheme: basic +paths: + /test: + get: + summary: 'Test endpoint' + operationId: 'testEndpoint' + security: [] + responses: + '200': + description: 'OK' +servers: + - url: 'https://example.com' +`; + const result = openApiToBruno(openApiWithEmptyOperationSecurity); + expect(result.items[0].request.auth.mode).toBe('inherit'); + }); + + it('should set auth mode to inherit for folder root when no security is defined', () => { + const openApiWithTags = ` +openapi: '3.0.0' +info: + version: '1.0.0' + title: 'API with tags' +paths: + /test: + get: + tags: + - TestGroup + summary: 'Test endpoint' + operationId: 'testEndpoint' + responses: + '200': + description: 'OK' +servers: + - url: 'https://example.com' +`; + const result = openApiToBruno(openApiWithTags); + expect(result.items[0].type).toBe('folder'); + expect(result.items[0].root.request.auth.mode).toBe('inherit'); + }); + }); }); const openApiCollectionString = ` @@ -174,7 +299,7 @@ const expectedOutput = { "basic": null, "bearer": null, "digest": null, - "mode": "none", + "mode": "inherit", }, "body": { "formUrlEncoded": [], From ce0fc08500f65cf0477438aec03c57f5eb297607 Mon Sep 17 00:00:00 2001 From: Pragadesh-45 <54320162+Pragadesh-45@users.noreply.github.com> Date: Thu, 14 Aug 2025 20:48:15 +0530 Subject: [PATCH 4/8] Feat/ Add Global Shortcuts for Zoom, Minimize, and Close on Windows (fixes: #4108) (#4110) Co-authored-by: sanjai0py --- .../src/providers/Hotkeys/keyMappings.js | 8 ++++---- packages/bruno-electron/src/index.js | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/bruno-app/src/providers/Hotkeys/keyMappings.js b/packages/bruno-app/src/providers/Hotkeys/keyMappings.js index 05ad4531b..b88300c8f 100644 --- a/packages/bruno-app/src/providers/Hotkeys/keyMappings.js +++ b/packages/bruno-app/src/providers/Hotkeys/keyMappings.js @@ -5,10 +5,10 @@ const KeyMapping = { newRequest: { mac: 'command+b', windows: 'ctrl+b', name: 'New Request' }, closeTab: { mac: 'command+w', windows: 'ctrl+w', name: 'Close Tab' }, openPreferences: { mac: 'command+,', windows: 'ctrl+,', name: 'Open Preferences' }, - minimizeWindow: { - mac: 'command+Shift+Q', - windows: 'control+Shift+Q', - name: 'Minimize Window' + closeBruno: { + mac: 'command+Q', + windows: 'ctrl+shift+q', + name: 'Close Bruno' }, switchToPreviousTab: { mac: 'command+pageup', diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index 047a35f31..3998e634d 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -11,7 +11,7 @@ if (isDev) { } const { format } = require('url'); -const { BrowserWindow, app, session, Menu, ipcMain } = require('electron'); +const { BrowserWindow, app, session, Menu, globalShortcut, ipcMain } = require('electron'); const { setContentSecurityPolicy } = require('electron-util'); if (isDev && process.env.ELECTRON_USER_DATA_PATH) { @@ -165,6 +165,19 @@ app.on('ready', async () => { } return { action: 'deny' }; }); + + // Quick fix for Electron issue #29996: https://github.com/electron/electron/issues/29996 + globalShortcut.register('Ctrl+=', () => { + mainWindow.webContents.setZoomLevel(mainWindow.webContents.getZoomLevel() + 1); + }); + + globalShortcut.register('CommandOrControl+M', () => { + mainWindow.minimize(); + }); + + globalShortcut.register('CommandOrControl+H', () => { + mainWindow.minimize(); + }); mainWindow.webContents.on('did-finish-load', () => { let ogSend = mainWindow.webContents.send; From b804ff6dfd3c5d37c8b2ab91a4fe00f3f0b54b69 Mon Sep 17 00:00:00 2001 From: naman-bruno Date: Tue, 19 Aug 2025 11:17:39 +0530 Subject: [PATCH 5/8] oauth2 fixes (#5259) --- packages/bruno-electron/src/utils/oauth2.js | 25 ++++++--------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/packages/bruno-electron/src/utils/oauth2.js b/packages/bruno-electron/src/utils/oauth2.js index fef0bcbd1..c5342b32a 100644 --- a/packages/bruno-electron/src/utils/oauth2.js +++ b/packages/bruno-electron/src/utils/oauth2.js @@ -259,15 +259,13 @@ const getOAuth2TokenUsingAuthorizationCode = async ({ request, collectionUid, fo redirect_uri: callbackUrl, client_id: clientId, }; - if (clientSecret && credentialsPlacement !== "basic_auth_header") { + if (clientSecret && clientSecret.trim() !== '' && credentialsPlacement !== "basic_auth_header") { data.client_secret = clientSecret; } if (pkce) { data['code_verifier'] = codeVerifier; } - if (scope && scope.trim() !== '') { - data.scope = scope; - } + axiosRequestConfig.data = qs.stringify(data); axiosRequestConfig.url = url; axiosRequestConfig.responseType = 'arraybuffer'; @@ -360,15 +358,6 @@ const getOAuth2TokenUsingClientCredentials = async ({ request, collectionUid, fo }; } - if (!clientSecret) { - return { - error: 'Client Secret is required for OAuth2 client credentials flow', - credentials: null, - url, - credentialsId - }; - } - if (!forceFetch) { const storedCredentials = getStoredOauth2Credentials({ collectionUid, url, credentialsId }); @@ -427,14 +416,14 @@ const getOAuth2TokenUsingClientCredentials = async ({ request, collectionUid, fo 'content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', }; - if (credentialsPlacement === "basic_auth_header") { + if (credentialsPlacement === "basic_auth_header" && clientSecret && clientSecret.trim() !== '') { axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; } const data = { grant_type: 'client_credentials', client_id: clientId, }; - if (clientSecret && credentialsPlacement !== "basic_auth_header") { + if (clientSecret && clientSecret.trim() !== '' && credentialsPlacement !== "basic_auth_header") { data.client_secret = clientSecret; } if (scope && scope.trim() !== '') { @@ -568,7 +557,7 @@ const getOAuth2TokenUsingPasswordCredentials = async ({ request, collectionUid, 'content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json', }; - if (credentialsPlacement === "basic_auth_header") { + if (credentialsPlacement === "basic_auth_header" && clientSecret && clientSecret.trim() !== '') { axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; } const data = { @@ -577,7 +566,7 @@ const getOAuth2TokenUsingPasswordCredentials = async ({ request, collectionUid, password, client_id: clientId, }; - if (clientSecret && credentialsPlacement !== "basic_auth_header") { + if (clientSecret && clientSecret.trim() !== '' && credentialsPlacement !== "basic_auth_header") { data.client_secret = clientSecret; } if (scope && scope.trim() !== '') { @@ -613,7 +602,7 @@ const refreshOauth2Token = async ({ requestCopy, collectionUid, certsAndProxyCon client_id: clientId, refresh_token: credentials.refresh_token, }; - if (clientSecret) { + if (clientSecret && clientSecret.trim() !== '') { data.client_secret = clientSecret; } let axiosRequestConfig = {}; From 060c613aa1da827a52f62e0a7d53e1c591cb8412 Mon Sep 17 00:00:00 2001 From: naman-bruno Date: Tue, 19 Aug 2025 14:21:00 +0530 Subject: [PATCH 6/8] fix: client id placement issue (#5348) --- packages/bruno-electron/src/utils/oauth2.js | 29 ++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/bruno-electron/src/utils/oauth2.js b/packages/bruno-electron/src/utils/oauth2.js index c5342b32a..c4e01e364 100644 --- a/packages/bruno-electron/src/utils/oauth2.js +++ b/packages/bruno-electron/src/utils/oauth2.js @@ -251,14 +251,16 @@ const getOAuth2TokenUsingAuthorizationCode = async ({ request, collectionUid, fo 'Accept': 'application/json', }; if (credentialsPlacement === "basic_auth_header") { - axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; + axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`).toString('base64')}`; } const data = { grant_type: 'authorization_code', code: authorizationCode, redirect_uri: callbackUrl, - client_id: clientId, }; + if (credentialsPlacement !== "basic_auth_header") { + data.client_id = clientId; + } if (clientSecret && clientSecret.trim() !== '' && credentialsPlacement !== "basic_auth_header") { data.client_secret = clientSecret; } @@ -417,12 +419,14 @@ const getOAuth2TokenUsingClientCredentials = async ({ request, collectionUid, fo 'Accept': 'application/json', }; if (credentialsPlacement === "basic_auth_header" && clientSecret && clientSecret.trim() !== '') { - axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; + axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`).toString('base64')}`; } const data = { grant_type: 'client_credentials', - client_id: clientId, }; + if (credentialsPlacement !== "basic_auth_header") { + data.client_id = clientId; + } if (clientSecret && clientSecret.trim() !== '' && credentialsPlacement !== "basic_auth_header") { data.client_secret = clientSecret; } @@ -558,14 +562,16 @@ const getOAuth2TokenUsingPasswordCredentials = async ({ request, collectionUid, 'Accept': 'application/json', }; if (credentialsPlacement === "basic_auth_header" && clientSecret && clientSecret.trim() !== '') { - axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; + axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`).toString('base64')}`; } const data = { grant_type: 'password', username, password, - client_id: clientId, }; + if (credentialsPlacement !== "basic_auth_header") { + data.client_id = clientId; + } if (clientSecret && clientSecret.trim() !== '' && credentialsPlacement !== "basic_auth_header") { data.client_secret = clientSecret; } @@ -588,7 +594,7 @@ const getOAuth2TokenUsingPasswordCredentials = async ({ request, collectionUid, const refreshOauth2Token = async ({ requestCopy, collectionUid, certsAndProxyConfig }) => { const oAuth = get(requestCopy, 'oauth2', {}); - const { clientId, clientSecret, credentialsId } = oAuth; + const { clientId, clientSecret, credentialsId, credentialsPlacement } = oAuth; const url = oAuth.refreshTokenUrl ? oAuth.refreshTokenUrl : oAuth.accessTokenUrl; const credentials = getStoredOauth2Credentials({ collectionUid, url, credentialsId }); @@ -599,10 +605,12 @@ const refreshOauth2Token = async ({ requestCopy, collectionUid, certsAndProxyCon } else { const data = { grant_type: 'refresh_token', - client_id: clientId, refresh_token: credentials.refresh_token, }; - if (clientSecret && clientSecret.trim() !== '') { + if (credentialsPlacement !== "basic_auth_header") { + data.client_id = clientId; + } + if (clientSecret && clientSecret.trim() !== '' && credentialsPlacement !== "basic_auth_header") { data.client_secret = clientSecret; } let axiosRequestConfig = {}; @@ -611,6 +619,9 @@ const refreshOauth2Token = async ({ requestCopy, collectionUid, certsAndProxyCon 'content-type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }; + if (credentialsPlacement === "basic_auth_header") { + axiosRequestConfig.headers['Authorization'] = `Basic ${Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`).toString('base64')}`; + } axiosRequestConfig.data = qs.stringify(data); axiosRequestConfig.url = url; axiosRequestConfig.responseType = 'arraybuffer'; From 77c96c4821690b3d611db3c241202169fc67d8c5 Mon Sep 17 00:00:00 2001 From: naman-bruno Date: Tue, 19 Aug 2025 15:24:05 +0530 Subject: [PATCH 7/8] fix: consider delay when running again (#5349) --- .../src/components/RunnerResults/index.jsx | 16 +++++++++++----- .../ReduxStore/slices/collections/actions.js | 5 +++-- .../ReduxStore/slices/collections/index.js | 5 +++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/bruno-app/src/components/RunnerResults/index.jsx b/packages/bruno-app/src/components/RunnerResults/index.jsx index 897d9d241..3071f2989 100644 --- a/packages/bruno-app/src/components/RunnerResults/index.jsx +++ b/packages/bruno-app/src/components/RunnerResults/index.jsx @@ -75,12 +75,15 @@ export default function RunnerResults({ collection }) { useEffect(() => { const savedConfiguration = get(collection, 'runnerConfiguration', null); - if (savedConfiguration && configureMode) { - if (savedConfiguration.selectedRequestItems) { + if (savedConfiguration) { + if (savedConfiguration.selectedRequestItems && configureMode) { setSelectedRequestItems(savedConfiguration.selectedRequestItems); } + if (savedConfiguration.delay !== undefined && delay === null) { + setDelay(savedConfiguration.delay); + } } - }, [collection.runnerConfiguration, configureMode]); + }, [collection.runnerConfiguration, configureMode, delay]); const collectionCopy = cloneDeep(collection); const runnerInfo = get(collection, 'runnerResult.info', {}); @@ -136,9 +139,10 @@ export default function RunnerResults({ collection }) { const runCollection = () => { if (configureMode && selectedRequestItems.length > 0) { - dispatch(updateRunnerConfiguration(collection.uid, selectedRequestItems, selectedRequestItems)); + dispatch(updateRunnerConfiguration(collection.uid, selectedRequestItems, selectedRequestItems, delay)); dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tagsEnabled && tags, selectedRequestItems)); } else { + dispatch(updateRunnerConfiguration(collection.uid, [], [], delay)); dispatch(runCollectionFolder(collection.uid, null, true, Number(delay), tagsEnabled && tags)); } }; @@ -148,12 +152,13 @@ export default function RunnerResults({ collection }) { // Get the saved configuration to determine what to run const savedConfiguration = get(collection, 'runnerConfiguration', null); const savedSelectedItems = savedConfiguration?.selectedRequestItems || []; + const savedDelay = savedConfiguration?.delay !== undefined ? savedConfiguration.delay : delay; dispatch( runCollectionFolder( collection.uid, runnerInfo.folderUid, true, - Number(delay), + Number(savedDelay), tagsEnabled && tags, savedSelectedItems ) @@ -168,6 +173,7 @@ export default function RunnerResults({ collection }) { ); setSelectedRequestItems([]); setConfigureMode(false); + setDelay(null); }; const cancelExecution = () => { 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 25cdbb93c..2bb166c91 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -1395,10 +1395,11 @@ export const mountCollection = ({ collectionUid, collectionPathname, brunoConfig }); }; -export const updateRunnerConfiguration = (collectionUid, selectedRequestItems, requestItemsOrder) => (dispatch) => { +export const updateRunnerConfiguration = (collectionUid, selectedRequestItems, requestItemsOrder, delay) => (dispatch) => { dispatch(_updateRunnerConfiguration({ collectionUid, selectedRequestItems, - requestItemsOrder + requestItemsOrder, + delay })); }; 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 3cd033124..850469279 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -2291,12 +2291,13 @@ export const collectionsSlice = createSlice({ } }, updateRunnerConfiguration: (state, action) => { - const { collectionUid, selectedRequestItems, requestItemsOrder } = action.payload; + const { collectionUid, selectedRequestItems, requestItemsOrder, delay } = action.payload; const collection = findCollectionByUid(state.collections, collectionUid); if (collection) { collection.runnerConfiguration = { selectedRequestItems: selectedRequestItems || [], - requestItemsOrder: requestItemsOrder || [] + requestItemsOrder: requestItemsOrder || [], + delay: delay }; } }, From 146c8462ea14fd2bf8f0880b42079b9bc9f6b211 Mon Sep 17 00:00:00 2001 From: lohit Date: Tue, 19 Aug 2025 15:24:23 +0530 Subject: [PATCH 8/8] option to parse large bru files using a regex based approach (#5324) --- .../RequestTabPanel/RequestNotLoaded/index.js | 24 ++---- .../ReduxStore/slices/collections/actions.js | 9 +++ .../src/app/collection-watcher.js | 33 ++++++-- packages/bruno-electron/src/ipc/collection.js | 59 +++++++++++++- packages/bruno-electron/src/utils/parse.js | 42 ++++++++++ .../bruno-filestore/src/formats/bru/index.ts | 4 +- .../input.bru | 66 ++++++++++++++++ .../output.bru | 35 +++++++++ ...request-parse-and-redact-body-data.spec.js | 44 +++++++++++ .../request-parse-and-redact-body-data.ts | 77 +++++++++++++++++++ packages/bruno-filestore/src/index.ts | 8 ++ 11 files changed, 370 insertions(+), 31 deletions(-) create mode 100644 packages/bruno-electron/src/utils/parse.js create mode 100644 packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/input.bru create mode 100644 packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/output.bru create mode 100644 packages/bruno-filestore/src/formats/bru/tests/request-parse-and-redact-body-data.spec.js create mode 100644 packages/bruno-filestore/src/formats/bru/utils/request-parse-and-redact-body-data.ts diff --git a/packages/bruno-app/src/components/RequestTabPanel/RequestNotLoaded/index.js b/packages/bruno-app/src/components/RequestTabPanel/RequestNotLoaded/index.js index 7908dfc09..08d011326 100644 --- a/packages/bruno-app/src/components/RequestTabPanel/RequestNotLoaded/index.js +++ b/packages/bruno-app/src/components/RequestTabPanel/RequestNotLoaded/index.js @@ -1,16 +1,13 @@ import { IconLoader2, IconFile, IconAlertTriangle } from '@tabler/icons'; -import { loadRequest, loadRequestViaWorker } from 'providers/ReduxStore/slices/collections/actions'; +import { loadLargeRequest } from 'providers/ReduxStore/slices/collections/actions'; import { useDispatch } from 'react-redux'; import StyledWrapper from './StyledWrapper'; const RequestNotLoaded = ({ collection, item }) => { const dispatch = useDispatch(); - const handleLoadRequestViaWorker = () => { - !item?.loading && dispatch(loadRequestViaWorker({ collectionUid: collection?.uid, pathname: item?.pathname })); - } - const handleLoadRequest = () => { - !item?.loading && dispatch(loadRequest({ collectionUid: collection?.uid, pathname: item?.pathname })); + const handleLoadLargeRequest = () => { + !item?.loading && dispatch(loadLargeRequest({ collectionUid: collection?.uid, pathname: item?.pathname })); } return @@ -44,23 +41,14 @@ const RequestNotLoaded = ({ collection, item }) => { The request wasn't loaded due to its large size. Please try again with the following options: -
- -

(Runs in background)

-
-

(May cause the app to freeze temporarily while it runs)

+

(Uses a regex based parsing approach)

)} 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 2bb166c91..7e3a758d3 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -1361,6 +1361,7 @@ export const clearOauth2Cache = (payload) => async (dispatch, getState) => { }); }; +// todo: could be removed export const loadRequestViaWorker = ({ collectionUid, pathname }) => (dispatch, getState) => { return new Promise(async (resolve, reject) => { const { ipcRenderer } = window; @@ -1368,6 +1369,7 @@ export const loadRequestViaWorker = ({ collectionUid, pathname }) => (dispatch, }); }; +// todo: could be removed export const loadRequest = ({ collectionUid, pathname }) => (dispatch, getState) => { return new Promise(async (resolve, reject) => { const { ipcRenderer } = window; @@ -1375,6 +1377,13 @@ export const loadRequest = ({ collectionUid, pathname }) => (dispatch, getState) }); }; +export const loadLargeRequest = ({ collectionUid, pathname }) => (dispatch, getState) => { + return new Promise(async (resolve, reject) => { + const { ipcRenderer } = window; + ipcRenderer.invoke('renderer:load-large-request', { collectionUid, pathname }).then(resolve).catch(reject); + }); +}; + export const mountCollection = ({ collectionUid, collectionPathname, brunoConfig }) => (dispatch, getState) => { dispatch(updateCollectionMountStatus({ collectionUid, mountStatus: 'mounting' })); return new Promise(async (resolve, reject) => { diff --git a/packages/bruno-electron/src/app/collection-watcher.js b/packages/bruno-electron/src/app/collection-watcher.js index acdb8873b..665792cdb 100644 --- a/packages/bruno-electron/src/app/collection-watcher.js +++ b/packages/bruno-electron/src/app/collection-watcher.js @@ -20,6 +20,7 @@ const { setBrunoConfig } = require('../store/bruno-config'); const EnvironmentSecretsStore = require('../store/env-secrets'); const UiStateSnapshot = require('../store/ui-state-snapshot'); const { parseBruFileMeta, hydrateRequestWithUuid } = require('../utils/collection'); +const { parseLargeRequestWithRedaction } = require('../utils/parse'); const MAX_FILE_SIZE = 2.5 * 1024 * 1024; @@ -344,11 +345,17 @@ const addDirectory = async (win, pathname, collectionUid, collectionPath) => { let seq; const folderBruFilePath = path.join(pathname, `folder.bru`); - if (fs.existsSync(folderBruFilePath)) { - let folderBruFileContent = fs.readFileSync(folderBruFilePath, 'utf8'); - let folderBruData = await parseFolder(folderBruFileContent); - name = folderBruData?.meta?.name || name; - seq = folderBruData?.meta?.seq; + try { + if (fs.existsSync(folderBruFilePath)) { + let folderBruFileContent = fs.readFileSync(folderBruFilePath, 'utf8'); + let folderBruData = await parseFolder(folderBruFileContent); + name = folderBruData?.meta?.name || name; + seq = folderBruData?.meta?.seq; + } + } + catch(error) { + console.error('Error occured while parsing folder.bru file!'); + console.error(error); } const directory = { @@ -462,10 +469,20 @@ const change = async (win, pathname, collectionUid, collectionPath) => { }; const bru = fs.readFileSync(pathname, 'utf8'); - file.data = await parseRequest(bru); + const fileStats = fs.statSync(pathname); - hydrateRequestWithUuid(file.data, pathname); - win.webContents.send('main:collection-tree-updated', 'change', file); + if (fileStats.size >= MAX_FILE_SIZE) { + const parsedData = await parseLargeRequestWithRedaction(bru); + file.data = parsedData; + file.size = sizeInMB(fileStats?.size); + hydrateRequestWithUuid(file.data, pathname); + win.webContents.send('main:collection-tree-updated', 'change', file); + } else { + file.data = await parseRequest(bru); + file.size = sizeInMB(fileStats?.size); + hydrateRequestWithUuid(file.data, pathname); + win.webContents.send('main:collection-tree-updated', 'change', file); + } } catch (err) { console.error(err); } diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 8ca5ac2f9..0d51b2f92 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -19,6 +19,7 @@ const { } = require('@usebruno/filestore'); const brunoConverters = require('@usebruno/converters'); const { postmanToBruno } = brunoConverters; +const { parseLargeRequestWithRedaction } = require('../utils/parse'); const { writeFile, @@ -1057,6 +1058,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }); + // todo: could be removed ipcMain.handle('renderer:load-request-via-worker', async (event, { collectionUid, pathname }) => { let fileStats; try { @@ -1094,7 +1096,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }; let bruContent = fs.readFileSync(pathname, 'utf8'); - const metaJson = parseRequest(parseBruFileMeta(bruContent)); + const metaJson = parseBruFileMeta(bruContent); file.data = metaJson; file.partial = true; file.loading = false; @@ -1132,6 +1134,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }); + // todo: could be removed ipcMain.handle('renderer:load-request', async (event, { collectionUid, pathname }) => { let fileStats; try { @@ -1145,7 +1148,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }; let bruContent = fs.readFileSync(pathname, 'utf8'); - const metaJson = parseRequest(parseBruFileMeta(bruContent)); + const metaJson = parseBruFileMeta(bruContent); file.data = metaJson; file.loading = true; file.partial = true; @@ -1169,7 +1172,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }; let bruContent = fs.readFileSync(pathname, 'utf8'); - const metaJson = parseRequest(parseBruFileMeta(bruContent)); + const metaJson = parseBruFileMeta(bruContent); file.data = metaJson; file.partial = true; file.loading = false; @@ -1181,6 +1184,56 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }); + ipcMain.handle('renderer:load-large-request', async (event, { collectionUid, pathname }) => { + let fileStats; + if (!hasBruExtension(pathname)) { + return; + } + + const file = { + meta: { + collectionUid, + pathname, + name: path.basename(pathname) + } + }; + + try { + fileStats = fs.statSync(pathname); + + const bruContent = fs.readFileSync(pathname, 'utf8'); + const metaJson = parseBruFileMeta(bruContent); + + file.data = metaJson; + file.partial = false; + file.loading = true; + file.size = sizeInMB(fileStats?.size); + hydrateRequestWithUuid(file.data, pathname); + await mainWindow.webContents.send('main:collection-tree-updated', 'addFile', file); + + try { + const parsedData = await parseLargeRequestWithRedaction(bruContent); + + file.data = parsedData; + file.loading = false; + file.partial = false; + file.size = sizeInMB(fileStats?.size); + hydrateRequestWithUuid(file.data, pathname); + await mainWindow.webContents.send('main:collection-tree-updated', 'addFile', file); + } catch (parseError) { + file.data = metaJson; + file.partial = true; + file.loading = false; + file.size = sizeInMB(fileStats?.size); + hydrateRequestWithUuid(file.data, pathname); + await mainWindow.webContents.send('main:collection-tree-updated', 'addFile', file); + throw parseError; + } + } catch (error) { + return Promise.reject(error); + } + }); + ipcMain.handle('renderer:mount-collection', async (event, { collectionUid, collectionPathname, brunoConfig }) => { const { size, diff --git a/packages/bruno-electron/src/utils/parse.js b/packages/bruno-electron/src/utils/parse.js new file mode 100644 index 000000000..655b9e6f7 --- /dev/null +++ b/packages/bruno-electron/src/utils/parse.js @@ -0,0 +1,42 @@ +const { parseRequestAndRedactBody, parseRequestViaWorker } = require('@usebruno/filestore'); + +/** + * Parses a large BRU request string by redacting body blocks, parsing the remainder, + * and then reinserting extracted body content into the parsed structure. + * @param {string} bruContent + * @returns {Promise} parsed request JSON + */ +async function parseLargeRequestWithRedaction(bruContent) { + const { bruFileStringWithRedactedBody, extractedBodyContent } = parseRequestAndRedactBody(bruContent); + const parsedData = await parseRequestViaWorker(bruFileStringWithRedactedBody); + + if (!parsedData.request) { + parsedData.request = {}; + } + if (!parsedData.request.body) { + parsedData.request.body = {}; + } + + if (extractedBodyContent.json) { + parsedData.request.body.json = extractedBodyContent.json; + } + if (extractedBodyContent.text) { + parsedData.request.body.text = extractedBodyContent.text; + } + if (extractedBodyContent.xml) { + parsedData.request.body.xml = extractedBodyContent.xml; + } + if (extractedBodyContent.sparql) { + parsedData.request.body.sparql = extractedBodyContent.sparql; + } + if (extractedBodyContent.graphql) { + if (!parsedData.request.body.graphql) { + parsedData.request.body.graphql = {}; + } + parsedData.request.body.graphql.query = extractedBodyContent.graphql; + } + + return parsedData; +} + +module.exports = { parseLargeRequestWithRedaction }; \ No newline at end of file diff --git a/packages/bruno-filestore/src/formats/bru/index.ts b/packages/bruno-filestore/src/formats/bru/index.ts index e71017cdf..f7bfff87c 100644 --- a/packages/bruno-filestore/src/formats/bru/index.ts +++ b/packages/bruno-filestore/src/formats/bru/index.ts @@ -47,8 +47,8 @@ export const bruRequestToJson = (data: string | any, parsed: boolean = false): a transformedJson.request.body.mode = _.get(json, 'http.body', 'none'); return transformedJson; - } catch (e) { - return Promise.reject(e); + } catch (error) { + throw error; } }; diff --git a/packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/input.bru b/packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/input.bru new file mode 100644 index 000000000..734e10d88 --- /dev/null +++ b/packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/input.bru @@ -0,0 +1,66 @@ +meta { + name: echo request + type: http + seq: 1 +} + +post { + url: https://echo.usebruno.com + body: json + auth: none +} +body:json { + { + "hello": "world" + } +} + +body:text { + This is a text body +} +body:xml { + + John + 30 + +} + +body:sparql { + SELECT * WHERE { + ?subject ?predicate ?object . + } + LIMIT 10 +} +body:graphql { + { + launchesPast { + launch_site { + site_name + } + launch_success + } + } +} + +body:form-urlencoded { + apikey: secret + numbers: +91998877665 + ~message: hello +} + +body:multipart-form { + apikey: secret + numbers: +91998877665 + ~message: hello +} +body: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) +} + +body:graphql:vars { + { + "limit": 5 + } +} diff --git a/packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/output.bru b/packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/output.bru new file mode 100644 index 000000000..ff66a2df8 --- /dev/null +++ b/packages/bruno-filestore/src/formats/bru/tests/fixtures/request-parse-and-redact-body-data/output.bru @@ -0,0 +1,35 @@ +meta { + name: echo request + type: http + seq: 1 +} + +post { + url: https://echo.usebruno.com + body: json + auth: none +} + +body:form-urlencoded { + apikey: secret + numbers: +91998877665 + ~message: hello +} + +body:multipart-form { + apikey: secret + numbers: +91998877665 + ~message: hello +} + +body: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) +} + +body:graphql:vars { + { + "limit": 5 + } +} diff --git a/packages/bruno-filestore/src/formats/bru/tests/request-parse-and-redact-body-data.spec.js b/packages/bruno-filestore/src/formats/bru/tests/request-parse-and-redact-body-data.spec.js new file mode 100644 index 000000000..aaed33015 --- /dev/null +++ b/packages/bruno-filestore/src/formats/bru/tests/request-parse-and-redact-body-data.spec.js @@ -0,0 +1,44 @@ +const fs = require('node:fs'); +const path = require('node:path'); +const { bruRequestParseAndRedactBodyData } = require("../utils/request-parse-and-redact-body-data"); + +describe("parse and redact body data", () => { + it("should redact body blocks from the bru file string", () => { + const fixturesPath = `/fixtures/request-parse-and-redact-body-data`; + const inputBruString = fs.readFileSync(path.join(__dirname, fixturesPath, './input.bru'), 'utf8'); + const expectedOutputBruString = fs.readFileSync(path.join(__dirname, fixturesPath, './output.bru'), 'utf8'); + + const res = bruRequestParseAndRedactBodyData(inputBruString); + expect(res.bruFileStringWithRedactedBody).toBe(expectedOutputBruString); + expect(res.extractedBodyContent).toEqual({ + graphql: ` +{ + launchesPast { + launch_site { + site_name + } + launch_success + } +} + `.trim(), + json: ` +{ + "hello": "world" +} + `.trim(), + sparql: ` +SELECT * WHERE { + ?subject ?predicate ?object . +} +LIMIT 10 + `.trim(), + text: `This is a text body`, + xml: ` + + John + 30 + + `.trim() + }) + }); +}); \ No newline at end of file diff --git a/packages/bruno-filestore/src/formats/bru/utils/request-parse-and-redact-body-data.ts b/packages/bruno-filestore/src/formats/bru/utils/request-parse-and-redact-body-data.ts new file mode 100644 index 000000000..6e2313575 --- /dev/null +++ b/packages/bruno-filestore/src/formats/bru/utils/request-parse-and-redact-body-data.ts @@ -0,0 +1,77 @@ +/** + * Parses a .bru file and extracts body content while redacting it from the main content + * @param {string} bruFileContent - The raw content of the .bru file + * @returns {Object} Object containing redacted file content and extracted body data + */ +export const bruRequestParseAndRedactBodyData = (bruFileContent: string) => { + try { + // Define the patterns that indicate the start of different body types + const bodyTypePatterns = [ + "body:json {", + "body:text {", + "body:xml {", + "body:sparql {", + "body:graphql {" + ]; + + // Normalize line endings to LF + bruFileContent = (bruFileContent || '').replace(/\r\n/g, '\n'); + + const EOL = `\n`; + + /** + * Removes the leading 2-space indentation from each line of a string + * @param {string} indentedString - The string with leading spaces to remove + * @returns {string} The string with indentation removed + */ + const removeLeadingIndentation = (indentedString: string) => { + if (!indentedString || !indentedString.length) { + return indentedString || ''; + } + + return indentedString + .split(EOL) + .map((line) => line.replace(/^ /, '')) + .join(EOL); + }; + + // Split the file content into blocks + let fileContentBlocks = bruFileContent.split(`${EOL}}${EOL}`); + fileContentBlocks = fileContentBlocks.filter(Boolean).map(_ => _.trim()); + + // Extract body blocks and their content + const extractedBodyBlocks = fileContentBlocks + .filter(block => bodyTypePatterns.some(pattern => block.startsWith(pattern))) + .reduce((bodyContentMap: Record, bodyBlock) => { + // Extract the body type (json, text, xml, etc.) from the first line + const firstLine = bodyBlock.split(EOL)[0]; + const bodyType = firstLine.split(`body:`)[1].split(/\s/)[0]; + + // Extract the body content (everything between the opening and closing braces) + const bodyContentLines = bodyBlock.split(EOL).slice(1); + const rawBodyContent = bodyContentLines.join(EOL); + + // Remove indentation from the body content + const cleanBodyContent = removeLeadingIndentation(rawBodyContent); + + bodyContentMap[bodyType] = cleanBodyContent; + return bodyContentMap; + }, {}); + + // Filter out body blocks to get the remaining file content + const fileContentWithoutBodyBlocks = fileContentBlocks.filter(block => + !bodyTypePatterns.some(pattern => block.startsWith(pattern)) + ); + + return { + bruFileStringWithRedactedBody: fileContentWithoutBodyBlocks.join(`${EOL}}${EOL}${EOL}`).concat(`${EOL}}${EOL}`), + extractedBodyContent: extractedBodyBlocks + }; + } catch (error) { + console.error('Error parsing and redacting body data:', error); + return { + bruFileStringWithRedactedBody: bruFileContent, + extractedBodyContent: {} + }; + } +}; \ No newline at end of file diff --git a/packages/bruno-filestore/src/index.ts b/packages/bruno-filestore/src/index.ts index 2caf3ac4f..2e1ec26d5 100644 --- a/packages/bruno-filestore/src/index.ts +++ b/packages/bruno-filestore/src/index.ts @@ -15,6 +15,7 @@ import { ParsedCollection, ParsedEnvironment } from './types'; +import { bruRequestParseAndRedactBodyData } from './formats/bru/utils/request-parse-and-redact-body-data'; export const parseRequest = (content: string, options: ParseOptions = { format: 'bru' }): any => { if (options.format === 'bru') { @@ -23,6 +24,13 @@ export const parseRequest = (content: string, options: ParseOptions = { format: throw new Error(`Unsupported format: ${options.format}`); }; +export const parseRequestAndRedactBody = (content: string, options: ParseOptions = { format: 'bru' }): any => { + if (options.format === 'bru') { + return bruRequestParseAndRedactBodyData(content); + } + throw new Error(`Unsupported format: ${options.format}`); +}; + export const stringifyRequest = (requestObj: ParsedRequest, options: StringifyOptions = { format: 'bru' }): string => { if (options.format === 'bru') { return jsonRequestToBru(requestObj);