diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index b005c6f1b..4a71c8cbd 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -19,31 +19,12 @@ const { prepareRequest } = require('./prepare-request'); const interpolateVars = require('./interpolate-vars'); const { makeAxiosInstance } = require('./axios-instance'); const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token'); -const { - uuid, - safeStringifyJSON, - safeParseJSON, - parseDataFromResponse, - parseDataFromRequest, -} = require('../../utils/common'); +const { uuid, safeStringifyJSON, safeParseJSON, parseDataFromResponse, parseDataFromRequest } = require('../../utils/common'); const { chooseFileToSave, writeBinaryFile, writeFile } = require('../../utils/filesystem'); const { addCookieToJar, getDomainsWithCookies, getCookieStringForUrl } = require('../../utils/cookies'); const { createFormData } = require('../../utils/form-data'); -const { - findItemInCollectionByPathname, - sortFolder, - getAllRequestsInFolderRecursively, - getEnvVars, - getTreePathFromCollectionToItem, - mergeVars, - sortByNameThenSequence, -} = require('../../utils/collection'); -const { - getOAuth2TokenUsingAuthorizationCode, - getOAuth2TokenUsingClientCredentials, - getOAuth2TokenUsingPasswordCredentials, - getOAuth2TokenUsingImplicitGrant, -} = require('../../utils/oauth2'); +const { findItemInCollectionByPathname, sortFolder, getAllRequestsInFolderRecursively, getEnvVars, getTreePathFromCollectionToItem, mergeVars, sortByNameThenSequence } = require('../../utils/collection'); +const { getOAuth2TokenUsingAuthorizationCode, getOAuth2TokenUsingClientCredentials, getOAuth2TokenUsingPasswordCredentials, getOAuth2TokenUsingImplicitGrant } = require('../../utils/oauth2'); const { preferencesUtil } = require('../../store/preferences'); const { getProcessEnvVars } = require('../../store/process-env'); const { getBrunoConfig } = require('../../store/bruno-config'); @@ -60,7 +41,9 @@ const saveCookies = (url, headers) => { if (preferencesUtil.shouldStoreCookies()) { let setCookieHeaders = []; if (headers['set-cookie']) { - setCookieHeaders = Array.isArray(headers['set-cookie']) ? headers['set-cookie'] : [headers['set-cookie']]; + setCookieHeaders = Array.isArray(headers['set-cookie']) + ? headers['set-cookie'] + : [headers['set-cookie']]; for (let setCookieHeader of setCookieHeaders) { if (typeof setCookieHeader === 'string' && setCookieHeader.length) { addCookieToJar(setCookieHeader, url); @@ -68,7 +51,7 @@ const saveCookies = (url, headers) => { } } } -}; +} const getJsSandboxRuntime = (collection) => { const securityConfig = get(collection, 'securityConfig', {}); @@ -84,7 +67,14 @@ const getJsSandboxRuntime = (collection) => { return 'vm2'; }; -const configureRequest = async (collectionUid, request, envVars, runtimeVariables, processEnvVars, collectionPath) => { +const configureRequest = async ( + collectionUid, + request, + envVars, + runtimeVariables, + processEnvVars, + collectionPath +) => { const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/; if (!protocolRegex.test(request.url)) { request.url = `http://${request.url}`; @@ -99,9 +89,9 @@ const configureRequest = async (collectionUid, request, envVars, runtimeVariable collectionPath }); - let requestMaxRedirects = request.maxRedirects; - request.maxRedirects = 0; - + let requestMaxRedirects = request.maxRedirects + request.maxRedirects = 0 + // Set default value for requestMaxRedirects if not explicitly set if (requestMaxRedirects === undefined) { requestMaxRedirects = 5; // Default to 5 redirects @@ -117,7 +107,7 @@ const configureRequest = async (collectionUid, request, envVars, runtimeVariable }); if (request.ntlmConfig) { - axiosInstance = NtlmClient(request.ntlmConfig, axiosInstance.defaults); + axiosInstance=NtlmClient(request.ntlmConfig,axiosInstance.defaults) delete request.ntlmConfig; } @@ -128,106 +118,66 @@ const configureRequest = async (collectionUid, request, envVars, runtimeVariable switch (grantType) { case 'authorization_code': interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - ({ - credentials, - url: oauth2Url, - credentialsId, - debugInfo, - } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid, certsAndProxyConfig })); - request.oauth2Credentials = { - credentials, - url: oauth2Url, - collectionUid, - credentialsId, - debugInfo, - folderUid: request.oauth2Credentials?.folderUid, - }; + ({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingAuthorizationCode({ request: requestCopy, collectionUid, certsAndProxyConfig })); + request.oauth2Credentials = { credentials, url: oauth2Url, collectionUid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid }; if (tokenPlacement == 'header' && credentials?.access_token) { request.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials.access_token}`.trim(); - } else { + } + else { try { const url = new URL(request.url); url?.searchParams?.set(tokenQueryKey, credentials?.access_token); request.url = url?.toString(); - } catch (error) {} + } + catch(error) {} } break; case 'implicit': interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - ({ - credentials, - url: oauth2Url, - credentialsId, - debugInfo, - } = await getOAuth2TokenUsingImplicitGrant({ request: requestCopy, collectionUid })); - request.oauth2Credentials = { - credentials, - url: oauth2Url, - collectionUid, - credentialsId, - debugInfo, - folderUid: request.oauth2Credentials?.folderUid, - }; + ({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingImplicitGrant({ request: requestCopy, collectionUid })); + request.oauth2Credentials = { credentials, url: oauth2Url, collectionUid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid }; if (tokenPlacement == 'header') { request.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials?.access_token}`; - } else { + } + else { try { const url = new URL(request.url); url?.searchParams?.set(tokenQueryKey, credentials?.access_token); request.url = url?.toString(); - } catch (error) {} + } + catch(error) {} } break; case 'client_credentials': interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - ({ - credentials, - url: oauth2Url, - credentialsId, - debugInfo, - } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid, certsAndProxyConfig })); - request.oauth2Credentials = { - credentials, - url: oauth2Url, - collectionUid, - credentialsId, - debugInfo, - folderUid: request.oauth2Credentials?.folderUid, - }; + ({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingClientCredentials({ request: requestCopy, collectionUid, certsAndProxyConfig })); + request.oauth2Credentials = { credentials, url: oauth2Url, collectionUid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid }; if (tokenPlacement == 'header' && credentials?.access_token) { request.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials.access_token}`.trim(); - } else { + } + else { try { const url = new URL(request.url); url?.searchParams?.set(tokenQueryKey, credentials?.access_token); request.url = url?.toString(); - } catch (error) {} + } + catch(error) {} } break; case 'password': interpolateVars(requestCopy, envVars, runtimeVariables, processEnvVars); - ({ - credentials, - url: oauth2Url, - credentialsId, - debugInfo, - } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid, certsAndProxyConfig })); - request.oauth2Credentials = { - credentials, - url: oauth2Url, - collectionUid, - credentialsId, - debugInfo, - folderUid: request.oauth2Credentials?.folderUid, - }; + ({ credentials, url: oauth2Url, credentialsId, debugInfo } = await getOAuth2TokenUsingPasswordCredentials({ request: requestCopy, collectionUid, certsAndProxyConfig })); + request.oauth2Credentials = { credentials, url: oauth2Url, collectionUid, credentialsId, debugInfo, folderUid: request.oauth2Credentials?.folderUid }; if (tokenPlacement == 'header' && credentials?.access_token) { request.headers['Authorization'] = `${tokenHeaderPrefix} ${credentials.access_token}`.trim(); - } else { + } + else { try { const url = new URL(request.url); url?.searchParams?.set(tokenQueryKey, credentials?.access_token); request.url = url?.toString(); - } catch (error) {} + } + catch(error) {} } break; } @@ -249,28 +199,29 @@ const configureRequest = async (collectionUid, request, envVars, runtimeVariable if (preferencesUtil.shouldSendCookies()) { const cookieString = getCookieStringForUrl(request.url); if (cookieString && typeof cookieString === 'string' && cookieString.length) { - const existingCookieHeaderName = Object.keys(request.headers).find(name => name.toLowerCase() === 'cookie'); + const existingCookieHeaderName = Object.keys(request.headers).find( + name => name.toLowerCase() === 'cookie' + ); const existingCookieString = existingCookieHeaderName ? request.headers[existingCookieHeaderName] : ''; - + // Helper function to parse cookies into an object - const parseCookies = str => - str.split(';').reduce((cookies, cookie) => { + const parseCookies = (str) => str.split(';').reduce((cookies, cookie) => { const [name, ...rest] = cookie.split('='); if (name && name.trim()) { - cookies[name.trim()] = rest.join('=').trim(); + cookies[name.trim()] = rest.join('=').trim(); } return cookies; - }, {}); - + }, {}); + const mergedCookies = { - ...parseCookies(existingCookieString), - ...parseCookies(cookieString), + ...parseCookies(existingCookieString), + ...parseCookies(cookieString), }; - + const combinedCookieString = Object.entries(mergedCookies) - .map(([name, value]) => `${name}=${value}`) - .join('; '); - + .map(([name, value]) => `${name}=${value}`) + .join('; '); + request.headers[existingCookieHeaderName || 'Cookie'] = combinedCookieString; } } @@ -378,15 +329,15 @@ const registerNetworkIpc = (mainWindow) => { }; const notifyScriptExecution = ({ - channel, // 'main:run-request-event' | 'main:run-folder-event' - basePayload, // request-level or runner-level identifiers - scriptType, // 'pre-request' | 'post-response' | 'test' - error, // optional Error + channel, // 'main:run-request-event' | 'main:run-folder-event' + basePayload, // request-level or runner-level identifiers + scriptType, // 'pre-request' | 'post-response' | 'test' + error // optional Error }) => { mainWindow.webContents.send(channel, { type: `${scriptType}-script-execution`, ...basePayload, - errorMessage: error ? error.message || `An error occurred in ${scriptType.replace('-', ' ')} script` : null, + errorMessage: error ? (error.message || `An error occurred in ${scriptType.replace('-', ' ')} script`) : null }); }; @@ -404,7 +355,7 @@ const registerNetworkIpc = (mainWindow) => { ) => { // run pre-request script let scriptResult; - const collectionName = collection?.name; + const collectionName = collection?.name const requestScript = get(request, 'script.req'); if (requestScript?.length) { const scriptRuntime = new ScriptRuntime({ runtime: scriptingConfig?.runtime }); @@ -531,7 +482,7 @@ const registerNetworkIpc = (mainWindow) => { // run post-response script const responseScript = get(request, 'script.res'); let scriptResult; - const collectionName = collection?.name; + const collectionName = collection?.name if (responseScript?.length) { const scriptRuntime = new ScriptRuntime({ runtime: scriptingConfig?.runtime }); scriptResult = await scriptRuntime.runResponseScript( @@ -572,14 +523,7 @@ const registerNetworkIpc = (mainWindow) => { return scriptResult; }; - const runRequest = async ({ - item, - collection, - envVars, - processEnvVars, - runtimeVariables, - runInBackground = false, - }) => { + const runRequest = async ({ item, collection, envVars, processEnvVars, runtimeVariables, runInBackground = false }) => { const collectionUid = collection.uid; const collectionPath = collection.pathname; const cancelTokenUid = uuid(); @@ -593,28 +537,20 @@ const registerNetworkIpc = (mainWindow) => { itemPathname = `${itemPathname}.bru`; } const _item = cloneDeep(findItemInCollectionByPathname(collection, itemPathname)); - if (_item) { - const res = await runRequest({ - item: _item, - collection, - envVars, - processEnvVars, - runtimeVariables, - runInBackground: true, - }); + if(_item) { + const res = await runRequest({ item: _item, collection, envVars, processEnvVars, runtimeVariables, runInBackground: true }); resolve(res); } reject(`bru.runRequest: invalid request path - ${itemPathname}`); }); - }; + } - !runInBackground - && mainWindow.webContents.send('main:run-request-event', { + !runInBackground && mainWindow.webContents.send('main:run-request-event', { type: 'request-queued', requestUid, collectionUid, itemUid: item.uid, - cancelTokenUid, + cancelTokenUid }); const abortController = new AbortController(); @@ -657,12 +593,11 @@ const registerNetworkIpc = (mainWindow) => { }); } - !runInBackground - && notifyScriptExecution({ + !runInBackground && notifyScriptExecution({ channel: 'main:run-request-event', basePayload: { requestUid, collectionUid, itemUid: item.uid }, scriptType: 'pre-request', - error: preRequestError, + error: preRequestError }); if (preRequestError) { @@ -684,16 +619,15 @@ const registerNetworkIpc = (mainWindow) => { headers: request.headers, data: requestData, dataBuffer: requestDataBuffer - }; + } - !runInBackground - && mainWindow.webContents.send('main:run-request-event', { + !runInBackground && mainWindow.webContents.send('main:run-request-event', { type: 'request-sent', requestSent, collectionUid, itemUid: item.uid, requestUid, - cancelTokenUid, + cancelTokenUid }); if (request?.oauth2Credentials) { @@ -702,9 +636,7 @@ const registerNetworkIpc = (mainWindow) => { url: request?.oauth2Credentials?.url, collectionUid, credentialsId: request?.oauth2Credentials?.credentialsId, - ...(request?.oauth2Credentials?.folderUid - ? { folderUid: request.oauth2Credentials.folderUid } - : { itemUid: item.uid }), + ...(request?.oauth2Credentials?.folderUid ? { folderUid: request.oauth2Credentials.folderUid } : { itemUid: item.uid }), debugInfo: request?.oauth2Credentials?.debugInfo, }); } @@ -747,7 +679,7 @@ const registerNetworkIpc = (mainWindow) => { statusText: error.statusText, error: error.message || ERROR_OCCURRED_WHILE_EXECUTING_REQUEST, timeline: error.timeline - }; + } } } @@ -790,7 +722,7 @@ const registerNetworkIpc = (mainWindow) => { console.error('Post-response script error:', error); postResponseError = error; } - + if (postResponseScriptResult?.results) { mainWindow.webContents.send('main:run-request-event', { type: 'test-results-post-response', @@ -801,12 +733,11 @@ const registerNetworkIpc = (mainWindow) => { }); } - !runInBackground - && notifyScriptExecution({ + !runInBackground && notifyScriptExecution({ channel: 'main:run-request-event', basePayload: { requestUid, collectionUid, itemUid: item.uid }, scriptType: 'post-response', - error: postResponseError, + error: postResponseError }); // run assertions @@ -822,18 +753,17 @@ const registerNetworkIpc = (mainWindow) => { processEnvVars ); - !runInBackground - && mainWindow.webContents.send('main:run-request-event', { + !runInBackground && mainWindow.webContents.send('main:run-request-event', { type: 'assertion-results', results: results, itemUid: item.uid, requestUid, - collectionUid, + collectionUid }); } const testFile = get(request, 'tests'); - const collectionName = collection?.name; + const collectionName = collection?.name if (typeof testFile === 'string') { const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime }); let testResults = null; @@ -855,7 +785,7 @@ const registerNetworkIpc = (mainWindow) => { ); } catch (error) { testError = error; - + if (error.partialResults) { testResults = error.partialResults; } else { @@ -870,13 +800,12 @@ const registerNetworkIpc = (mainWindow) => { } } - !runInBackground - && mainWindow.webContents.send('main:run-request-event', { + !runInBackground && mainWindow.webContents.send('main:run-request-event', { type: 'test-results', results: testResults.results, itemUid: item.uid, requestUid, - collectionUid, + collectionUid }); mainWindow.webContents.send('main:script-environment-update', { @@ -897,12 +826,11 @@ const registerNetworkIpc = (mainWindow) => { collection.globalEnvironmentVariables = testResults.globalEnvironmentVariables; - !runInBackground - && notifyScriptExecution({ + !runInBackground && notifyScriptExecution({ channel: 'main:run-request-event', basePayload: { requestUid, collectionUid, itemUid: item.uid }, scriptType: 'test', - error: testError, + error: testError }); const domainsWithCookiesTest = await getDomainsWithCookies(); @@ -932,7 +860,7 @@ const registerNetworkIpc = (mainWindow) => { timeline: error?.timeline }; } - }; + } // handler for sending http request ipcMain.handle('send-http-request', async (event, item, collection, environment, runtimeVariables) => { @@ -967,7 +895,7 @@ const registerNetworkIpc = (mainWindow) => { }); // handler for fetch-gql-schema - ipcMain.handle('fetch-gql-schema', fetchGqlSchemaHandler); + ipcMain.handle('fetch-gql-schema', fetchGqlSchemaHandler) ipcMain.handle( 'renderer:run-collection-folder', @@ -993,20 +921,13 @@ const registerNetworkIpc = (mainWindow) => { itemPathname = `${itemPathname}.bru`; } const _item = cloneDeep(findItemInCollectionByPathname(collection, itemPathname)); - if (_item) { - const res = await runRequest({ - item: _item, - collection, - envVars, - processEnvVars, - runtimeVariables, - runInBackground: true, - }); + if(_item) { + const res = await runRequest({ item: _item, collection, envVars, processEnvVars, runtimeVariables, runInBackground: true }); resolve(res); } reject(`bru.runRequest: invalid request path - ${itemPathname}`); }); - }; + } if (!folder) { folder = collection; @@ -1033,8 +954,9 @@ const registerNetworkIpc = (mainWindow) => { } }); + // sort requests by seq property - folderRequests = sortByNameThenSequence(folderRequests); + folderRequests = sortByNameThenSequence(folderRequests) } // Filter requests based on tags @@ -1043,7 +965,7 @@ const registerNetworkIpc = (mainWindow) => { const excludeTags = tags.exclude ? tags.exclude : []; folderRequests = folderRequests.filter(({ tags: requestTags = [], draft }) => { requestTags = draft?.tags || requestTags || []; - return isRequestTagsIncluded(requestTags, includeTags, excludeTags); + return isRequestTagsIncluded(requestTags, includeTags, excludeTags) }); } @@ -1096,7 +1018,7 @@ const registerNetworkIpc = (mainWindow) => { const request = await prepareRequest(item, collection, abortController); request.__bruno__executionMode = 'runner'; - + const requestUid = uuid(); try { @@ -1136,8 +1058,7 @@ const registerNetworkIpc = (mainWindow) => { }); const domainsWithCookiesPreRequest = await getDomainsWithCookies(); - mainWindow.webContents.send('main:cookies-update', - safeParseJSON(safeStringifyJSON(domainsWithCookiesPreRequest))); + mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookiesPreRequest))); if (preRequestError) { throw preRequestError; @@ -1175,7 +1096,7 @@ const registerNetworkIpc = (mainWindow) => { headers: request.headers, data: requestData, dataBuffer: requestDataBuffer - }; + } // todo: // i have no clue why electron can't send the request object @@ -1202,9 +1123,7 @@ const registerNetworkIpc = (mainWindow) => { url: request?.oauth2Credentials?.url, collectionUid, credentialsId: request?.oauth2Credentials?.credentialsId, - ...(request?.oauth2Credentials?.folderUid - ? { folderUid: request.oauth2Credentials.folderUid } - : { itemUid: item.uid }), + ...(request?.oauth2Credentials?.folderUid ? { folderUid: request.oauth2Credentials.folderUid } : { itemUid: item.uid }), debugInfo: request?.oauth2Credentials?.debugInfo, }); } @@ -1256,11 +1175,9 @@ const registerNetworkIpc = (mainWindow) => { data: response.data, responseTime: response.responseTime, timeline: response.timeline, - url: response.request - ? response.request.protocol + '//' + response.request.host + response.request.path - : null, + url: response.request ? response.request.protocol + '//' + response.request.host + response.request.path : null }, - ...eventData, + ...eventData }); } catch (error) { // Skip further processing if request was cancelled @@ -1332,8 +1249,7 @@ const registerNetworkIpc = (mainWindow) => { }); const domainsWithCookiesPostResponse = await getDomainsWithCookies(); - mainWindow.webContents.send('main:cookies-update', - safeParseJSON(safeStringifyJSON(domainsWithCookiesPostResponse))); + mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookiesPostResponse))); if (postResponseScriptResult?.nextRequestName !== undefined) { nextRequestName = postResponseScriptResult.nextRequestName; @@ -1374,7 +1290,7 @@ const registerNetworkIpc = (mainWindow) => { } const testFile = get(request, 'tests'); - const collectionName = collection?.name; + const collectionName = collection?.name if (typeof testFile === 'string') { let testResults = null; let testError = null; @@ -1396,7 +1312,7 @@ const registerNetworkIpc = (mainWindow) => { ); } catch (error) { testError = error; - + if (error.partialResults) { testResults = error.partialResults; } else { @@ -1430,7 +1346,7 @@ const registerNetworkIpc = (mainWindow) => { mainWindow.webContents.send('main:global-environment-variables-update', { globalEnvironmentVariables: testResults.globalEnvironmentVariables }); - + collection.globalEnvironmentVariables = testResults.globalEnvironmentVariables; notifyScriptExecution({ @@ -1441,8 +1357,7 @@ const registerNetworkIpc = (mainWindow) => { }); const domainsWithCookiesTest = await getDomainsWithCookies(); - mainWindow.webContents.send('main:cookies-update', - safeParseJSON(safeStringifyJSON(domainsWithCookiesTest))); + mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookiesTest))); } } catch (error) { mainWindow.webContents.send('main:run-folder-event', { @@ -1493,7 +1408,7 @@ const registerNetworkIpc = (mainWindow) => { runCompletionTime: new Date().toISOString(), }); } catch (error) { - console.log('error', error); + console.log("error", error); deleteCancelToken(cancelTokenUid); mainWindow.webContents.send('main:run-folder-event', { type: 'testrun-ended', @@ -1525,7 +1440,7 @@ const registerNetworkIpc = (mainWindow) => { try { const disposition = contentDispositionParser.parse(contentDisposition); return disposition && disposition.parameters['filename']; - } catch (error) {} + } catch (error) { } }; const getFileNameFromUrlPath = () => { @@ -1557,7 +1472,7 @@ const registerNetworkIpc = (mainWindow) => { const filePath = await chooseFileToSave(mainWindow, fileName); if (filePath) { const encoding = getEncodingFormat(); - const data = Buffer.from(response.dataBuffer, 'base64'); + const data = Buffer.from(response.dataBuffer, 'base64') if (encoding === 'utf-8') { await writeFile(filePath, data); } else { @@ -1589,13 +1504,14 @@ const executeRequestOnFailHandler = async (request, error) => { } }; + const registerAllNetworkIpc = (mainWindow) => { registerNetworkIpc(mainWindow); registerGrpcEventHandlers(mainWindow); registerWsEventHandlers(mainWindow); -}; +} -module.exports = registerAllNetworkIpc; +module.exports = registerAllNetworkIpc module.exports.configureRequest = configureRequest; module.exports.getCertsAndProxyConfig = getCertsAndProxyConfig; module.exports.fetchGqlSchemaHandler = fetchGqlSchemaHandler;