From b982f6db16ca8819d4c51262a82baa4f877d5c7f Mon Sep 17 00:00:00 2001 From: sanish-bruno Date: Sat, 27 Sep 2025 04:58:35 +0530 Subject: [PATCH] refactor: replace grpc-reflection-js with grpc-js-reflection-client in grpc-client implementation rm: comment fix: type generation feat: implement reflection client support for gRPC v1 and v1alpha in grpc-client refactor: simplify reflection client handling in grpc-client by removing service list retrieval refactor: enhance reflection client return structure in grpc-client to include service list fix: lint --- package-lock.json | 53 +++---------- packages/bruno-requests/package.json | 4 +- .../bruno-requests/src/grpc/grpc-client.js | 79 ++++++++++++------- 3 files changed, 65 insertions(+), 71 deletions(-) diff --git a/package-lock.json b/package-lock.json index decb6c7cc..6b6f35f34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8232,12 +8232,6 @@ "@types/node": "*" } }, - "node_modules/@types/google-protobuf": { - "version": "3.15.12", - "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz", - "integrity": "sha512-40um9QqwHjRS92qnOaDpL7RmDK15NuZYo9HihiJRbYkMQZlWnuH8AdvbMy8/o6lgLmKbDUKa+OALCltHdbOTpQ==", - "license": "MIT" - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -8373,9 +8367,9 @@ "license": "MIT" }, "node_modules/@types/lodash": { - "version": "4.17.13", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.13.tgz", - "integrity": "sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==", + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", "license": "MIT" }, "node_modules/@types/lodash-es": { @@ -8388,15 +8382,6 @@ "@types/lodash": "*" } }, - "node_modules/@types/lodash.set": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/@types/lodash.set/-/lodash.set-4.3.9.tgz", - "integrity": "sha512-KOxyNkZpbaggVmqbpr82N2tDVTx05/3/j0f50Es1prxrWB0XYf9p3QNxqcbWb7P1Q9wlvsUSlCFnwlPCIJ46PQ==", - "license": "MIT", - "dependencies": { - "@types/lodash": "*" - } - }, "node_modules/@types/markdown-it": { "version": "12.2.3", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", @@ -15182,12 +15167,6 @@ "csstype": "^3.0.10" } }, - "node_modules/google-protobuf": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", - "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", - "license": "(BSD-3-Clause AND Apache-2.0)" - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -15312,20 +15291,18 @@ "node": ">= 6" } }, - "node_modules/grpc-reflection-js": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/grpc-reflection-js/-/grpc-reflection-js-0.3.0.tgz", - "integrity": "sha512-3lhTlQluPxVgbowCXA3tAZC3RJW+GSOUkguLNYl1QffYRiutUB3RDfPkQFTcrCFJgNiIIxx+iJkr8s3uSp3zWA==", + "node_modules/grpc-js-reflection-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/grpc-js-reflection-client/-/grpc-js-reflection-client-1.3.0.tgz", + "integrity": "sha512-eJ5/m1pXpcheSjOGExktU69WPUKnL4Su3IxGJYYYjy3/w19vE8dH7Wi46G5T92bpM0eZWftjiM5HduX8CjPq9w==", "license": "MIT", "dependencies": { - "@types/google-protobuf": "^3.7.2", - "@types/lodash.set": "^4.3.6", - "google-protobuf": "^3.12.2", - "lodash.set": "^4.3.2", - "protobufjs": "^7.2.2" + "@types/lodash": "^4.17.15", + "lodash": "^4.17.21", + "protobufjs": "^7.4.0" }, "peerDependencies": { - "@grpc/grpc-js": "^1.0.0" + "@grpc/grpc-js": "^1.12.6" } }, "node_modules/har-schema": { @@ -18634,12 +18611,6 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, - "node_modules/lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==", - "license": "MIT" - }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -32129,7 +32100,7 @@ "@types/qs": "^6.9.18", "axios": "^1.9.0", "debug": "^4.4.3", - "grpc-reflection-js": "^0.3.0", + "grpc-js-reflection-client": "^1.3.0", "is-ip": "^5.0.1", "system-ca": "^2.0.1", "tough-cookie": "^6.0.0", diff --git a/packages/bruno-requests/package.json b/packages/bruno-requests/package.json index 1d54c53e7..630b873ef 100644 --- a/packages/bruno-requests/package.json +++ b/packages/bruno-requests/package.json @@ -26,7 +26,7 @@ "@types/qs": "^6.9.18", "axios": "^1.9.0", "debug": "^4.4.3", - "grpc-reflection-js": "^0.3.0", + "grpc-js-reflection-client": "^1.3.0", "is-ip": "^5.0.1", "ws": "^8.18.3", "system-ca": "^2.0.1", @@ -53,4 +53,4 @@ "overrides": { "rollup": "3.29.5" } -} +} \ No newline at end of file diff --git a/packages/bruno-requests/src/grpc/grpc-client.js b/packages/bruno-requests/src/grpc/grpc-client.js index ab4c6cac1..585277ec6 100644 --- a/packages/bruno-requests/src/grpc/grpc-client.js +++ b/packages/bruno-requests/src/grpc/grpc-client.js @@ -1,5 +1,5 @@ -import { makeGenericClientConstructor, ChannelCredentials, Metadata } from '@grpc/grpc-js'; -import * as grpcReflection from 'grpc-reflection-js'; +import { makeGenericClientConstructor, ChannelCredentials, Metadata, status } from '@grpc/grpc-js'; +import { GrpcReflection } from 'grpc-js-reflection-client'; import * as protoLoader from '@grpc/proto-loader'; import { generateGrpcSampleMessage } from './grpcMessageGenerator'; import * as tls from 'tls'; @@ -34,6 +34,8 @@ const configOptions = { json: true }; +const reflectionServices = ['grpc.reflection.v1alpha.ServerReflection', 'grpc.reflection.v1.ServerReflection']; + const replaceTabsWithSpaces = (str, numSpaces = 2) => { if (!str || !str.length || !isString(str)) { return ''; @@ -172,6 +174,32 @@ class GrpcClient { this.eventCallback = eventCallback; } + /** + * Creates a reflection client that works for v1, v1alpha, or both. + * + * @param {string} host - host:port of the gRPC server + * @param {grpc.ChannelCredentials} credentials - defaults to insecure + * @param {grpc.ChannelOptions} options - channel options + * @returns {Promise<{ client: GrpcReflection, version: 'v1' | 'v1alpha' }>} + */ + async #getReflectionClient(host, credentials = ChannelCredentials.createInsecure(), options = {}) { + const makeClient = (version) => new GrpcReflection(host, credentials, options, version); + let client; + let services; + + try { + client = makeClient('v1'); + services = await client.listServices(); + return { client, services }; + } catch (e) { + console.warn(`gRPC reflection v1 failed:`, e); + } + + client = makeClient('v1alpha'); + services = await client.listServices(); + return { client, services }; + } + /** * Get method type based on streaming configuration */ @@ -582,31 +610,29 @@ class GrpcClient { }); try { - const client = new grpcReflection.Client(host, credentials, {}, metadata); + const { client, services } = await this.#getReflectionClient(host, credentials, {}); + const methods = []; - const declarations = await client.listServices(); - const methods = await Promise.all( - declarations.map(async (declaration) => { - const fileContainingSymbol = await client.fileContainingSymbol(declaration); - const descriptor = fileContainingSymbol.toDescriptor('proto3'); - const protoDefinition = protoLoader.loadFileDescriptorSetFromObject(descriptor, configOptions); + for (const service of services) { + if (reflectionServices.includes(service)) { + continue; + } + const m = await client.listMethods(service); + methods.push(...m); + } - const serviceDefinition = protoDefinition[declaration]; - if (!!serviceDefinition?.format) { - return []; - } - const methods = Object.values(serviceDefinition); - methods.forEach((method) => { - this.methods.set(method.path, method); - }); - return methods; - }) - ); - - const methodsWithType = methods.flat().map((method) => ({ - ...method, - type: this.#getMethodType(method) - })); + const methodsWithType = methods.map((method) => { + const { definition, ...rest } = method; + const modifiedMethod = { + ...rest, + ...definition + }; + modifiedMethod.type = this.#getMethodType(modifiedMethod); + return modifiedMethod; + }); + methodsWithType.forEach((method) => { + this.methods.set(method.path, method); + }); return methodsWithType; } catch (error) { console.error('Error in gRPC reflection:', error); @@ -615,9 +641,6 @@ class GrpcClient { } } - /** - * Load methods from proto file - */ async loadMethodsFromProtoFile(filePath, includeDirs = []) { const protoDefinition = await protoLoader.load(filePath, { ...configOptions, includeDirs }); const methods = Object.values(protoDefinition)