1+ import { isObject , isUndefined } from '@nestjs/common/utils/shared.utils' ;
12import { fromEvent } from 'rxjs' ;
23import { takeUntil } from 'rxjs/operators' ;
34import {
@@ -29,13 +30,11 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
2930 this . getOptionsProp < GrpcOptions > ( options , 'url' ) || GRPC_DEFAULT_URL ;
3031
3132 const protoLoader =
32- this . getOptionsProp < GrpcOptions > ( options , 'protoLoader' ) || GRPC_DEFAULT_PROTO_LOADER ;
33+ this . getOptionsProp < GrpcOptions > ( options , 'protoLoader' ) ||
34+ GRPC_DEFAULT_PROTO_LOADER ;
3335
3436 grpcPackage = this . loadPackage ( 'grpc' , ServerGrpc . name ) ;
35- grpcProtoLoaderPackage = this . loadPackage (
36- protoLoader ,
37- ServerGrpc . name ,
38- ) ;
37+ grpcProtoLoaderPackage = this . loadPackage ( protoLoader , ServerGrpc . name ) ;
3938 }
4039
4140 public async listen ( callback : ( ) => void ) {
@@ -61,16 +60,30 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
6160 this . logger . error ( invalidPackageError . message , invalidPackageError . stack ) ;
6261 throw invalidPackageError ;
6362 }
64- for ( const name of this . getServiceNames ( grpcPkg ) ) {
63+
64+ // Take all of the services defined in grpcPkg and assign them to
65+ // method handlers defined in Controllers
66+ for ( const definition of this . getServiceNames ( grpcPkg ) ) {
6567 this . grpcClient . addService (
66- grpcPkg [ name ] . service ,
67- await this . createService ( grpcPkg [ name ] , name ) ,
68+ // First parameter requires exact service definition from proto
69+ definition . service . service ,
70+ // Here full proto definition required along with namespaced pattern name
71+ await this . createService ( definition . service , definition . name ) ,
6872 ) ;
6973 }
7074 }
7175
72- public getServiceNames ( grpcPkg : any ) {
73- return Object . keys ( grpcPkg ) . filter ( name => grpcPkg [ name ] . service ) ;
76+ /**
77+ * Will return all of the services along with their fully namespaced
78+ * names as an array of objects.
79+ * This method initiates recursive scan of grpcPkg object
80+ */
81+ public getServiceNames ( grpcPkg : any ) : { name : string ; service : any } [ ] {
82+ // Define accumulator to collect all of the services available to load
83+ const services : { name : string ; service : any } [ ] = [ ] ;
84+ // Initiate recursive services collector starting with empty name
85+ this . collectDeepServices ( '' , grpcPkg , services ) ;
86+ return services ;
7487 }
7588
7689 public async createService ( grpcService : any , name : string ) {
@@ -186,4 +199,58 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
186199 throw invalidProtoError ;
187200 }
188201 }
202+
203+ /**
204+ * Recursively fetch all of the service methods available on loaded
205+ * protobuf descriptor object, and collect those as an objects with
206+ * dot-syntax full-path names.
207+ *
208+ * Example:
209+ * for proto package Bundle.FirstService with service Events { rpc...
210+ * will be resolved to object of (while loaded for Bundle package):
211+ * {
212+ * name: "FirstService.Events",
213+ * service: {Object}
214+ * }
215+ */
216+ private collectDeepServices (
217+ name : string ,
218+ grpcDefinition : any ,
219+ accumulator : { name : string ; service : any } [ ] ,
220+ ) {
221+ if ( ! isObject ( grpcDefinition ) ) {
222+ return ;
223+ }
224+ const keysToTraverse = Object . keys ( grpcDefinition ) ;
225+ // Traverse definitions or namespace extensions
226+ for ( const key of keysToTraverse ) {
227+ const nameExtended = this . parseDeepServiceName ( name , key ) ;
228+ const deepDefinition = grpcDefinition [ key ] ;
229+
230+ const isServiceDefined = ! isUndefined ( deepDefinition . service ) ;
231+ const isServiceBoolean = isServiceDefined
232+ ? deepDefinition . service !== false
233+ : false ;
234+
235+ if ( isServiceDefined && isServiceBoolean ) {
236+ accumulator . push ( {
237+ name : nameExtended ,
238+ service : deepDefinition ,
239+ } ) ;
240+ }
241+ // Continue recursion until objects end or service definition found
242+ else {
243+ this . collectDeepServices ( nameExtended , deepDefinition , accumulator ) ;
244+ }
245+ }
246+ }
247+
248+ private parseDeepServiceName ( name : string , key : string ) : string {
249+ // If depth is zero then just return key
250+ if ( name . length === 0 ) {
251+ return key ;
252+ }
253+ // Otherwise add next through dot syntax
254+ return name + '.' + key ;
255+ }
189256}
0 commit comments