1- import { ChannelCredentials , Metadata , ServiceError } from '@postman/grpc-js' ;
2- import { getDescriptorRootFromDescriptorSet } from './descriptor' ;
3- import * as services from './reflection_grpc_pb' ;
41import {
5- ServerReflectionRequest ,
6- ServerReflectionResponse ,
7- } from './reflection_pb' ;
2+ ChannelCredentials ,
3+ Metadata ,
4+ ServiceError ,
5+ status as GrpcStatus ,
6+ } from '@postman/grpc-js' ;
7+ import { getDescriptorRootFromDescriptorSet } from './descriptor' ;
88import { Root } from '@postman/protobufjs' ;
99import {
1010 FileDescriptorSet ,
@@ -13,26 +13,147 @@ import {
1313} from '@postman/protobufjs/ext/descriptor' ;
1414import set from 'lodash.set' ;
1515
16+ // Static type definitions with common structures across all reflection providers
17+ import type { ServerReflectionClient } from './reflection_providers/v1alpha/reflection_grpc_pb' ;
18+ import type {
19+ ServerReflectionRequest ,
20+ ServerReflectionResponse ,
21+ } from './reflection_providers/v1alpha/reflection_pb' ;
22+
23+ const supportedReflectionProtocols = [
24+ {
25+ protocol : 'v1alpha' ,
26+ serviceName : 'grpc.reflection.v1alpha.ServerReflection' ,
27+ client : import ( './reflection_providers/v1alpha/reflection_pb' ) ,
28+ service : import ( './reflection_providers/v1alpha/reflection_grpc_pb' ) ,
29+ } ,
30+ {
31+ protocol : 'v1' ,
32+ serviceName : 'grpc.reflection.v1.ServerReflection' ,
33+ client : import ( './reflection_providers/v1/reflection_pb' ) ,
34+ service : import ( './reflection_providers/v1/reflection_grpc_pb' ) ,
35+ } ,
36+ ] ;
37+
1638export class Client {
1739 metadata : Metadata ;
18- grpcClient : services . IServerReflectionClient ;
1940 private fileDescriptorCache : Map < string , IFileDescriptorProto > = new Map ( ) ;
41+ private url : string ;
42+ private credentials : ChannelCredentials ;
43+ private clientOptions : object | undefined ;
44+
45+ grpcClient : ServerReflectionClient | undefined ;
46+ private reflectionResponseCache : ServerReflectionResponse | undefined ;
47+ private compatibleProtocol : string | undefined ;
48+ private CompatibleServerReflectionRequest :
49+ | ( new (
50+ ...args : ConstructorParameters < typeof ServerReflectionRequest >
51+ ) => ServerReflectionRequest )
52+ | undefined ;
53+
2054 constructor (
2155 url : string ,
2256 credentials : ChannelCredentials ,
2357 options ?: object ,
2458 metadata ?: Metadata
2559 ) {
60+ this . url = url ;
61+ this . credentials = credentials ;
62+ this . clientOptions = options ;
2663 this . fileDescriptorCache = new Map ( ) ;
2764 this . metadata = metadata || new Metadata ( ) ;
28- this . grpcClient = new services . ServerReflectionClient (
29- url ,
30- credentials ,
31- options
32- ) ;
3365 }
3466
35- listServices ( ) : Promise < string [ ] > {
67+ private async sendReflectionRequest (
68+ request : ServerReflectionRequest | ServerReflectionRequest [ ] ,
69+ client ?: ServerReflectionClient
70+ ) : Promise < ServerReflectionResponse [ ] > {
71+ return new Promise ( ( resolve , reject ) => {
72+ const result : ServerReflectionResponse [ ] = [ ] ;
73+
74+ const grpcCall = ( client || this . grpcClient ! ) . serverReflectionInfo (
75+ this . metadata
76+ ) ;
77+
78+ grpcCall . on ( 'data' , ( response : ServerReflectionResponse ) => {
79+ result . push ( response ) ;
80+ } ) ;
81+
82+ grpcCall . on ( 'error' , ( error : ServiceError ) => {
83+ reject ( error ) ;
84+ } ) ;
85+
86+ grpcCall . on ( 'end' , ( ) => resolve ( result ) ) ;
87+
88+ if ( Array . isArray ( request ) ) {
89+ request . forEach ( req => grpcCall . write ( req ) ) ;
90+ } else {
91+ grpcCall . write ( request ) ;
92+ }
93+
94+ grpcCall . end ( ) ;
95+ } ) ;
96+ }
97+
98+ private async initializeReflectionClient ( ) {
99+ if ( this . grpcClient || this . compatibleProtocol ) {
100+ return ;
101+ }
102+
103+ // Check protocol compatibility and initialize gRPC client based on that
104+ for ( const protocolConfig of supportedReflectionProtocols ) {
105+ const {
106+ protocol,
107+ service : servicePromise ,
108+ client : clientPromise ,
109+ } = protocolConfig ;
110+
111+ const [ protocolService , protocolClient ] = await Promise . all ( [
112+ servicePromise ,
113+ clientPromise ,
114+ ] ) ;
115+
116+ const grpcClientForProtocol = new protocolService . ServerReflectionClient (
117+ this . url ,
118+ this . credentials ,
119+ this . clientOptions
120+ ) ;
121+
122+ const request = new protocolClient . ServerReflectionRequest ( ) ;
123+
124+ request . setListServices ( '*' ) ;
125+
126+ try {
127+ const [ reflectionResponse ] = await this . sendReflectionRequest (
128+ request ,
129+ grpcClientForProtocol
130+ ) ;
131+
132+ this . grpcClient = grpcClientForProtocol ;
133+ this . compatibleProtocol = protocol ;
134+ this . CompatibleServerReflectionRequest =
135+ protocolClient . ServerReflectionRequest ;
136+ this . reflectionResponseCache = reflectionResponse ;
137+
138+ break ; // Exit loop on first successful protocol
139+ } catch ( error ) {
140+ if ( ( error as ServiceError ) . code !== GrpcStatus . UNIMPLEMENTED ) {
141+ // Something is wrong with the gRPC server itself
142+ throw error ;
143+ }
144+
145+ continue ; // Try next protocol
146+ }
147+ }
148+
149+ if ( ! this . grpcClient ) {
150+ throw new Error ( 'No compatible reflection protocol found.' ) ;
151+ }
152+ }
153+
154+ async listServices ( ) : Promise < string [ ] > {
155+ await this . initializeReflectionClient ( ) ;
156+
36157 return new Promise ( ( resolve , reject ) => {
37158 function dataCallback ( response : ServerReflectionResponse ) {
38159 if ( response . hasListServicesResponse ( ) ) {
@@ -52,14 +173,16 @@ export class Client {
52173 reject ( e ) ;
53174 }
54175
55- const request = new ServerReflectionRequest ( ) ;
176+ if ( this . reflectionResponseCache ) {
177+ return dataCallback ( this . reflectionResponseCache ) ;
178+ }
179+
180+ const request = new this . CompatibleServerReflectionRequest ! ( ) ;
56181 request . setListServices ( '*' ) ;
57182
58- const grpcCall = this . grpcClient . serverReflectionInfo ( this . metadata ) ;
59- grpcCall . on ( 'data' , dataCallback ) ;
60- grpcCall . on ( 'error' , errorCallback ) ;
61- grpcCall . write ( request ) ;
62- grpcCall . end ( ) ;
183+ this . sendReflectionRequest ( request )
184+ . then ( ( [ response ] ) => dataCallback ( response ) )
185+ . catch ( errorCallback ) ;
63186 } ) ;
64187 }
65188
@@ -120,9 +243,11 @@ export class Client {
120243 return fileDescriptorMap ;
121244 }
122245
123- private getFileContainingSymbol (
246+ private async getFileContainingSymbol (
124247 symbol : string
125248 ) : Promise < Array < IFileDescriptorProto > | undefined > {
249+ await this . initializeReflectionClient ( ) ;
250+
126251 const fileDescriptorCache = this . fileDescriptorCache ;
127252 return new Promise ( ( resolve , reject ) => {
128253 function dataCallback ( response : ServerReflectionResponse ) {
@@ -154,20 +279,20 @@ export class Client {
154279 reject ( e ) ;
155280 }
156281
157- const request = new ServerReflectionRequest ( ) ;
282+ const request = new this . CompatibleServerReflectionRequest ! ( ) ;
158283 request . setFileContainingSymbol ( symbol ) ;
159284
160- const grpcCall = this . grpcClient . serverReflectionInfo ( this . metadata ) ;
161- grpcCall . on ( 'data' , dataCallback ) ;
162- grpcCall . on ( 'error' , errorCallback ) ;
163- grpcCall . write ( request ) ;
164- grpcCall . end ( ) ;
285+ this . sendReflectionRequest ( request )
286+ . then ( ( [ response ] ) => dataCallback ( response ) )
287+ . catch ( errorCallback ) ;
165288 } ) ;
166289 }
167290
168- private getFilesByFilenames (
291+ private async getFilesByFilenames (
169292 symbols : string [ ]
170293 ) : Promise < Array < IFileDescriptorProto > | undefined > {
294+ await this . initializeReflectionClient ( ) ;
295+
171296 const result : Array < IFileDescriptorProto > = [ ] ;
172297 const fileDescriptorCache = this . fileDescriptorCache ;
173298 const symbolsToFetch = symbols . filter ( symbol => {
@@ -203,6 +328,13 @@ export class Client {
203328 result . push ( fileDescriptorProto ) ;
204329 }
205330 } ) ;
331+ } else if ( response . hasErrorResponse ( ) ) {
332+ const err = response . getErrorResponse ( ) ;
333+ reject (
334+ new Error (
335+ `Error: ${ err ?. getErrorCode ( ) } : ${ err ?. getErrorMessage ( ) } `
336+ )
337+ ) ;
206338 } else {
207339 reject ( Error ( ) ) ;
208340 }
@@ -212,19 +344,17 @@ export class Client {
212344 reject ( e ) ;
213345 }
214346
215- const grpcCall = this . grpcClient . serverReflectionInfo ( this . metadata ) ;
216- grpcCall . on ( 'data' , dataCallback ) ;
217- grpcCall . on ( 'error' , errorCallback ) ;
218- grpcCall . on ( 'end' , ( ) => {
219- resolve ( result ) ;
347+ const requests = symbolsToFetch . map ( symbol => {
348+ const request = new this . CompatibleServerReflectionRequest ! ( ) ;
349+ return request . setFileByFilename ( symbol ) ;
220350 } ) ;
221351
222- symbolsToFetch . forEach ( symbol => {
223- const request = new ServerReflectionRequest ( ) ;
224- grpcCall . write ( request . setFileByFilename ( symbol ) ) ;
225- } ) ;
226-
227- grpcCall . end ( ) ;
352+ this . sendReflectionRequest ( requests )
353+ . then ( responses => {
354+ for ( const dataBit of responses ) dataCallback ( dataBit ) ;
355+ resolve ( result ) ;
356+ } )
357+ . catch ( errorCallback ) ;
228358 } ) ;
229359 }
230360}
0 commit comments