1+ import { isObject , isUndefined } from '@nestjs/common/utils/shared.utils' ;
12import { fromEvent } from 'rxjs' ;
23import { takeUntil } from 'rxjs/operators' ;
3- import { CANCEL_EVENT , GRPC_DEFAULT_PROTO_LOADER , GRPC_DEFAULT_URL } from '../constants' ;
4+ import {
5+ CANCEL_EVENT ,
6+ GRPC_DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH ,
7+ GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH ,
8+ GRPC_DEFAULT_PROTO_LOADER ,
9+ GRPC_DEFAULT_URL
10+ } from '../constants' ;
411import { InvalidGrpcPackageException } from '../exceptions/errors/invalid-grpc-package.exception' ;
512import { InvalidProtoDefinitionException } from '../exceptions/errors/invalid-proto-definition.exception' ;
613import { CustomTransportStrategy } from '../interfaces' ;
@@ -23,13 +30,11 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
2330 this . getOptionsProp < GrpcOptions > ( options , 'url' ) || GRPC_DEFAULT_URL ;
2431
2532 const protoLoader =
26- this . getOptionsProp < GrpcOptions > ( options , 'protoLoader' ) || GRPC_DEFAULT_PROTO_LOADER ;
33+ this . getOptionsProp < GrpcOptions > ( options , 'protoLoader' ) ||
34+ GRPC_DEFAULT_PROTO_LOADER ;
2735
2836 grpcPackage = this . loadPackage ( 'grpc' , ServerGrpc . name ) ;
29- grpcProtoLoaderPackage = this . loadPackage (
30- protoLoader ,
31- ServerGrpc . name ,
32- ) ;
37+ grpcProtoLoaderPackage = this . loadPackage ( protoLoader , ServerGrpc . name ) ;
3338 }
3439
3540 public async listen ( callback : ( ) => void ) {
@@ -55,16 +60,30 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
5560 this . logger . error ( invalidPackageError . message , invalidPackageError . stack ) ;
5661 throw invalidPackageError ;
5762 }
58- 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 ) ) {
5967 this . grpcClient . addService (
60- grpcPkg [ name ] . service ,
61- 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 ) ,
6272 ) ;
6373 }
6474 }
6575
66- public getServiceNames ( grpcPkg : any ) {
67- 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 ;
6887 }
6988
7089 public async createService ( grpcService : any , name : string ) {
@@ -137,7 +156,10 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
137156 }
138157
139158 public createClient ( ) : any {
140- const server = new grpcPackage . Server ( ) ;
159+ const server = new grpcPackage . Server ( {
160+ 'grpc.max_send_message_length' : this . getOptionsProp < GrpcOptions > ( this . options , 'maxSendMessageLength' , GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH ) ,
161+ 'grpc.max_receive_message_length' : this . getOptionsProp < GrpcOptions > ( this . options , 'maxReceiveMessageLength' , GRPC_DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH )
162+ } ) ;
141163 const credentials = this . getOptionsProp < GrpcOptions > (
142164 this . options ,
143165 'credentials' ,
@@ -177,4 +199,58 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
177199 throw invalidProtoError ;
178200 }
179201 }
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+ }
180256}
0 commit comments