diff --git a/packages/bruno-app/src/utils/network/index.js b/packages/bruno-app/src/utils/network/index.js index 0cf24d4ae..9eca0d4cd 100644 --- a/packages/bruno-app/src/utils/network/index.js +++ b/packages/bruno-app/src/utils/network/index.js @@ -1,6 +1,3 @@ -import cloneDeep from 'lodash/cloneDeep'; -import { resolvePath } from 'utils/filesystem'; - export const sendNetworkRequest = async (item, collection, environment, runtimeVariables) => { return new Promise((resolve, reject) => { if (['http-request', 'graphql-request'].includes(item.type)) { @@ -143,25 +140,10 @@ export const endGrpcStream = async (requestId) => { }; export const loadGrpcMethodsFromProtoFile = async (filePath, collection = null) => { - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { const { ipcRenderer } = window; - // Extract import paths from collection's gRPC config if available - let importPaths = []; - - if (collection) { - const config = cloneDeep(collection.brunoConfig); - - if (config.protobuf && config.protobuf.importPaths) { - // Use Promise.all to wait for all resolvePath calls to complete - const enabledImportPaths = config.protobuf.importPaths.filter((importPath) => importPath.enabled); - importPaths = await Promise.all(enabledImportPaths.map((importPath) => { - return resolvePath(importPath.path, collection.pathname); - })); - } - } - - ipcRenderer.invoke('grpc:load-methods-proto', { filePath, includeDirs: importPaths }).then(resolve).catch(reject); + ipcRenderer.invoke('grpc:load-methods-proto', { filePath, collection }).then(resolve).catch(reject); }); }; diff --git a/packages/bruno-electron/src/ipc/network/grpc-event-handlers.js b/packages/bruno-electron/src/ipc/network/grpc-event-handlers.js index b0794acbb..919bd3b1c 100644 --- a/packages/bruno-electron/src/ipc/network/grpc-event-handlers.js +++ b/packages/bruno-electron/src/ipc/network/grpc-event-handlers.js @@ -8,11 +8,30 @@ const { getCertsAndProxyConfig } = require('./cert-utils'); const { interpolateString } = require('./interpolate-string'); const path = require('node:path'); const prepareGrpcRequest = require('./prepare-grpc-request'); +const { normalizeAndResolvePath } = require('../../utils/filesystem'); const { configureRequest } = require('./prepare-grpc-request'); // Creating grpcClient at module level so it can be accessed from window-all-closed event let grpcClient; +/** + * Extract protobuf include directories from collection config + * @param {Object} collection - The collection object + * @returns {string[]} Array of resolved include directory paths + */ +const getProtobufIncludeDirs = (collection) => { + if (!collection) { + return []; + } + + const brunoConfig = collection.draft?.brunoConfig || collection.brunoConfig; + const importPaths = brunoConfig?.protobuf?.importPaths ?? []; + + return importPaths + .filter(({ enabled }) => Boolean(enabled)) + .map(({ path: relativePath }) => normalizeAndResolvePath(path.resolve(collection.pathname, relativePath))); +}; + /** * Register IPC handlers for gRPC */ @@ -89,6 +108,9 @@ const registerGrpcEventHandlers = (window) => { body: preparedRequest.body, timestamp: Date.now() }; + + const includeDirs = getProtobufIncludeDirs(collection); + // Start gRPC connection with the processed request and certificates await grpcClient.startConnection({ request: preparedRequest, @@ -98,7 +120,8 @@ const registerGrpcEventHandlers = (window) => { certificateChain, passphrase, pfx, - verifyOptions + verifyOptions, + includeDirs }); sendEvent('grpc:request', preparedRequest.uid, collection.uid, requestSent); @@ -260,8 +283,10 @@ const registerGrpcEventHandlers = (window) => { }); // Load methods from proto file - ipcMain.handle('grpc:load-methods-proto', async (event, { filePath, includeDirs }) => { + ipcMain.handle('grpc:load-methods-proto', async (event, { filePath, collection }) => { try { + const includeDirs = getProtobufIncludeDirs(collection); + const methods = await grpcClient.loadMethodsFromProtoFile(filePath, includeDirs); return { success: true, methods: safeParseJSON(safeStringifyJSON(methods)) }; } catch (error) { diff --git a/packages/bruno-requests/src/grpc/grpc-client.js b/packages/bruno-requests/src/grpc/grpc-client.js index 695be3eec..00a9b8f72 100644 --- a/packages/bruno-requests/src/grpc/grpc-client.js +++ b/packages/bruno-requests/src/grpc/grpc-client.js @@ -316,10 +316,11 @@ class GrpcClient { * @param {string} [options.collectionUid] - Collection UID * @param {Object} [options.certificates] - Certificate configuration * @param {Object} [options.verifyOptions] - Additional options for verifying the server certificate + * @param {string[]} [options.includeDirs] - Include directories for proto file resolution * @returns {Promise} Whether methods were successfully refreshed * @private */ - async #refreshMethods({ url, headers, protoPath, collectionPath, collectionUid, certificates = {}, verifyOptions }) { + async #refreshMethods({ url, headers, protoPath, collectionPath, collectionUid, certificates = {}, verifyOptions, includeDirs = [] }) { try { // Try reflection first if no proto path is specified if (!protoPath) { @@ -339,8 +340,8 @@ class GrpcClient { // Try proto file if available if (protoPath) { - const absoluteProtoPath = nodePath.join(collectionPath, protoPath); - await this.loadMethodsFromProtoFile(absoluteProtoPath, []); + const absoluteProtoPath = nodePath.resolve(collectionPath, protoPath); + await this.loadMethodsFromProtoFile(absoluteProtoPath, includeDirs); return true; } @@ -463,6 +464,7 @@ class GrpcClient { * @param {string} [params.pfx] - The PFX/P12 certificate data * @param {Object} [params.verifyOptions] - Additional options for verifying the server certificate * @param {import('@grpc/grpc-js').ChannelOptions} [params.channelOptions] - Additional options for the gRPC channel + * @param {string[]} [params.includeDirs] - Include directories for proto file resolution */ async startConnection({ request, @@ -473,7 +475,8 @@ class GrpcClient { passphrase, pfx, verifyOptions, - channelOptions = {} + channelOptions = {}, + includeDirs = [] }) { const credentials = this.#getChannelCredentials({ url: request.url, @@ -509,7 +512,8 @@ class GrpcClient { passphrase, pfx }, - verifyOptions + verifyOptions, + includeDirs }); if (!refreshSuccess) {