From 22a77b90f95355e2571d41c24ac7f269ece46fbf Mon Sep 17 00:00:00 2001 From: sanish chirayath Date: Mon, 25 Aug 2025 15:17:15 +0530 Subject: [PATCH] Enhance gRPC request handling in collection transformation functions by conditionally including methodType and protoPath, and removing params for gRPC requests. (#5399) --- .../bruno-app/src/utils/collections/index.js | 11 +- .../bruno-app/src/utils/importers/common.js | 5 + .../collections/grpc-export-import.spec.js | 221 ++++++++++++++++++ 3 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 packages/bruno-app/src/utils/tests/collections/grpc-export-import.spec.js diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index dbe73966d..c7e0817b0 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -232,6 +232,8 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} return; } + const isGrpcRequest = si.type === 'grpc-request' + const di = { uid: si.uid, type: si.type, @@ -246,8 +248,6 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} di.request = { url: si.request.url, method: si.request.method, - methodType: si.request.methodType, - protoPath: si.request.protoPath, headers: copyHeaders(si.request.headers), params: copyParams(si.request.params), body: { @@ -269,6 +269,13 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {} docs: si.request.docs }; + if (isGrpcRequest) { + di.request.methodType = si.request.methodType; + di.request.protoPath = si.request.protoPath; + delete di.request.params; + } + + // Handle auth object dynamically di.request.auth = { mode: get(si.request, 'auth.mode', 'none') diff --git a/packages/bruno-app/src/utils/importers/common.js b/packages/bruno-app/src/utils/importers/common.js index fab237be0..5d3b21d72 100644 --- a/packages/bruno-app/src/utils/importers/common.js +++ b/packages/bruno-app/src/utils/importers/common.js @@ -64,6 +64,7 @@ export const transformItemsInCollection = (collection) => { each(items, (item) => { if (['http', 'graphql', 'grpc'].includes(item.type)) { item.type = `${item.type}-request`; + const isGrpcRequest = item.type === 'grpc-request'; if (item.request.query) { item.request.params = item.request.query.map((queryItem) => ({ @@ -73,6 +74,10 @@ export const transformItemsInCollection = (collection) => { })); } + if (isGrpcRequest) { + delete item.request.params; + } + delete item.request.query; // from 5 feb 2024, multipartFormData needs to have a type diff --git a/packages/bruno-app/src/utils/tests/collections/grpc-export-import.spec.js b/packages/bruno-app/src/utils/tests/collections/grpc-export-import.spec.js new file mode 100644 index 000000000..9596e50d4 --- /dev/null +++ b/packages/bruno-app/src/utils/tests/collections/grpc-export-import.spec.js @@ -0,0 +1,221 @@ +import { transformCollectionToSaveToExportAsFile, transformRequestToSaveToFilesystem } from '../../collections/index'; +import { transformItemsInCollection } from '../../importers/common'; + +describe('gRPC Export/Import', () => { + describe('transformCollectionToSaveToExportAsFile', () => { + it('should preserve gRPC-specific fields when exporting collection', () => { + const collection = { + uid: 'test-collection', + name: 'Test Collection', + items: [ + { + uid: 'grpc-request-1', + type: 'grpc-request', + name: 'Test gRPC Request', + request: { + url: 'grpc://localhost:50051', + method: '/randomService/randomMethod', + methodType: 'unary', + protoPath: 'proto/service.proto', + headers: [], + body: { + mode: 'grpc', + grpc: [{ name: 'message', content: '{}' }] + } + } + } + ] + }; + + const result = transformCollectionToSaveToExportAsFile(collection); + const grpcRequest = result.items[0]; + + expect(grpcRequest.request.methodType).toBe('unary'); + expect(grpcRequest.request.method).toBe('/randomService/randomMethod'); + expect(grpcRequest.request.protoPath).toBe('proto/service.proto'); + expect(grpcRequest.request.params).toBeUndefined(); + }); + + it('should handle different gRPC method types correctly', () => { + const collection = { + uid: 'test-collection', + name: 'Test Collection', + items: [ + { + uid: 'grpc-request-1', + type: 'grpc-request', + name: 'Streaming Request', + request: { + url: 'grpc://localhost:50051', + method: '/randomService/randomMethod', + methodType: 'bidi-streaming', + protoPath: 'proto/streaming.proto', + headers: [], + body: { mode: 'grpc', grpc: [] } + } + } + ] + }; + + const result = transformCollectionToSaveToExportAsFile(collection); + const grpcRequest = result.items[0]; + + expect(grpcRequest.request.methodType).toBe('bidi-streaming'); + expect(grpcRequest.request.method).toBe('/randomService/randomMethod'); + expect(grpcRequest.request.protoPath).toBe('proto/streaming.proto'); + }); + + it('should handle gRPC requests without method', () => { + const collection = { + uid: 'test-collection', + name: 'Test Collection', + items: [ + { + uid: 'grpc-request-1', + type: 'grpc-request', + name: 'Streaming Request', + request: { + url: 'grpc://localhost:50051', + methodType: 'unary', + headers: [], + body: { mode: 'grpc', grpc: [] } + } + } + ] + }; + + const result = transformCollectionToSaveToExportAsFile(collection); + const grpcRequest = result.items[0]; + + expect(grpcRequest.request.methodType).toBe('unary'); + expect(grpcRequest.request.method).toBeUndefined(); + expect(grpcRequest.request.protoPath).toBeUndefined(); + }); + }); + + describe('transformRequestToSaveToFilesystem', () => { + it('should preserve gRPC fields and remove params for gRPC requests', () => { + const grpcRequest = { + uid: 'grpc-request-1', + type: 'grpc-request', + name: 'Test gRPC', + request: { + url: 'grpc://localhost:50051', + method: '/randomService/randomMethod', + methodType: 'server-streaming', + protoPath: 'proto/service.proto', + params: [{ uid: 'param-1', name: 'test', value: 'value' }], + headers: [], + body: { mode: 'grpc', grpc: [] } + } + }; + + const result = transformRequestToSaveToFilesystem(grpcRequest); + + expect(result.request.methodType).toBe('server-streaming'); + expect(result.request.protoPath).toBe('proto/service.proto'); + expect(result.request.params).toBeUndefined(); + }); + + it('should not remove params for non-gRPC requests', () => { + const httpRequest = { + uid: 'http-request-1', + type: 'http-request', + name: 'Test HTTP', + request: { + url: 'http://localhost:3000', + method: 'GET', + params: [{ uid: 'param-1', name: 'test', value: 'value' }], + headers: [], + body: { mode: 'json', json: '{}' } + } + }; + + const result = transformRequestToSaveToFilesystem(httpRequest); + + expect(result.request.params).toHaveLength(1); + expect(result.request.params[0].name).toBe('test'); + }); + }); + + describe('transformItemsInCollection', () => { + it('should transform gRPC request type correctly during import', () => { + const collection = { + uid: 'test-collection', + items: [ + { + uid: 'grpc-request-1', + type: 'grpc', + name: 'Test gRPC', + request: { + url: 'grpc://localhost:50051', + methodType: 'unary', + protoPath: 'proto/service.proto', + body: { mode: 'grpc', grpc: [] } + } + } + ] + }; + + transformItemsInCollection(collection); + const grpcRequest = collection.items[0]; + + expect(grpcRequest.type).toBe('grpc-request'); + expect(grpcRequest.request.methodType).toBe('unary'); + expect(grpcRequest.request.protoPath).toBe('proto/service.proto'); + }); + + it('should handle gRPC requests without protoPath', () => { + const collection = { + uid: 'test-collection', + items: [ + { + uid: 'grpc-request-1', + type: 'grpc', + name: 'Test gRPC', + request: { + url: 'grpc://localhost:50051', + method: '/randomService/randomMethod', + methodType: 'client-streaming', + body: { mode: 'grpc', grpc: [] } + } + } + ] + }; + + transformItemsInCollection(collection); + const grpcRequest = collection.items[0]; + + expect(grpcRequest.type).toBe('grpc-request'); + expect(grpcRequest.request.methodType).toBe('client-streaming'); + expect(grpcRequest.request.protoPath).toBeUndefined(); + }); + + it('should handle gRPC requests without method', () => { + const collection = { + uid: 'test-collection', + items: [ + { + uid: 'grpc-request-1', + type: 'grpc', + name: 'Test gRPC', + request: { + url: 'grpc://localhost:50051', + methodType: 'unary', + protoPath: 'proto/service.proto', + body: { mode: 'grpc', grpc: [] } + } + } + ] + }; + + transformItemsInCollection(collection); + const grpcRequest = collection.items[0]; + + expect(grpcRequest.type).toBe('grpc-request'); + expect(grpcRequest.request.method).toBeUndefined(); + expect(grpcRequest.request.methodType).toBe('unary'); + expect(grpcRequest.request.protoPath).toBe('proto/service.proto'); + }); + }); +});