Skip to content

Commit 5eee77e

Browse files
Merge branch '5.6.0'
2 parents c90b8e3 + c448e56 commit 5eee77e

9 files changed

Lines changed: 207 additions & 40 deletions

File tree

package-lock.json

Lines changed: 32 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
"license": "MIT",
3333
"dependencies": {
3434
"@grpc/proto-loader": "^0.3.0",
35-
"@nestjs/common": "5.1.0",
36-
"@nestjs/core": "^5.3.10",
37-
"@nestjs/microservices": "5.1.0",
35+
"@nestjs/common": "5.5.0",
36+
"@nestjs/core": "^5.5.0",
37+
"@nestjs/microservices": "5.5.0",
3838
"@nestjs/testing": "5.1.0",
3939
"@nestjs/websockets": "5.1.0",
4040
"@nuxtjs/opencollective": "^0.1.0",

packages/common/http/http.module.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ export class HttpModule {
4747
imports: options.imports,
4848
providers: [
4949
...this.createAsyncProviders(options),
50+
{
51+
provide: AXIOS_INSTANCE_TOKEN,
52+
useFactory: (config: HttpModuleOptions) => Axios.create(config),
53+
inject: [HTTP_MODULE_OPTIONS],
54+
},
5055
{
5156
provide: HTTP_MODULE_ID,
5257
useValue: randomStringGenerator(),

packages/common/interfaces/microservices/microservice-configuration.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export interface GrpcOptions {
2020
transport?: Transport.GRPC;
2121
options: {
2222
url?: string;
23+
maxSendMessageLength?: number;
24+
maxReceiveMessageLength?: number;
2325
credentials?: any;
2426
protoPath: string;
2527
package: string;

packages/microservices/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,5 @@ export const RQM_DEFAULT_IS_GLOBAL_PREFETCH_COUNT = false;
2727
export const RQM_DEFAULT_QUEUE_OPTIONS = {};
2828

2929
export const GRPC_DEFAULT_PROTO_LOADER = '@grpc/proto-loader';
30+
export const GRPC_DEFAULT_MAX_RECEIVE_MESSAGE_LENGTH = 4 * 1024 * 1024;
31+
export const GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH = 4 * 1024 * 1024;

packages/microservices/interfaces/microservice-configuration.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export interface GrpcOptions {
2121
transport?: Transport.GRPC;
2222
options: {
2323
url?: string;
24+
maxSendMessageLength?: number;
25+
maxReceiveMessageLength?: number;
2426
credentials?: any;
2527
protoPath: string;
2628
package: string;

packages/microservices/server/server-grpc.ts

Lines changed: 88 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
import { isObject, isUndefined } from '@nestjs/common/utils/shared.utils';
12
import { fromEvent } from 'rxjs';
23
import { 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';
411
import { InvalidGrpcPackageException } from '../exceptions/errors/invalid-grpc-package.exception';
512
import { InvalidProtoDefinitionException } from '../exceptions/errors/invalid-proto-definition.exception';
613
import { 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
}

packages/microservices/server/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export abstract class Server {
6363
prop: keyof T['options'],
6464
defaultValue = undefined,
6565
) {
66-
return obj ? obj[prop as string] : defaultValue;
66+
return obj && obj[prop as string] || defaultValue;
6767
}
6868

6969
protected handleError(error: string) {

packages/microservices/test/server/server-grpc.spec.ts

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,16 @@ describe('ServerGrpc', () => {
5151
});
5252
describe('when package exist', () => {
5353
it('should call "addService"', async () => {
54-
const serviceNames = ['test', 'test2'];
54+
const serviceNames = [{
55+
name: 'test',
56+
service: true
57+
}, {
58+
name: 'test2',
59+
service: true
60+
}];
5561
sinon.stub(server, 'lookupPackage').callsFake(() => ({
56-
test: true,
57-
test2: true,
62+
test: {service: true},
63+
test2: {service: true}
5864
}));
5965
sinon.stub(server, 'getServiceNames').callsFake(() => serviceNames);
6066

@@ -73,7 +79,16 @@ describe('ServerGrpc', () => {
7379
key2: { service: true },
7480
key3: { service: false },
7581
};
76-
const expected = ['key', 'key2'];
82+
const expected = [
83+
{
84+
name: 'key',
85+
service: {service: true}
86+
},
87+
{
88+
name: 'key2',
89+
service: {service: true}
90+
}
91+
];
7792
expect(server.getServiceNames(obj)).to.be.eql(expected);
7893
});
7994
});
@@ -225,4 +240,57 @@ describe('ServerGrpc', () => {
225240
expect(server.deserialize(content)).to.equal(content);
226241
});
227242
});
243+
244+
describe('proto interfaces parser should account for package namespaces', () => {
245+
it('should parse multi-level proto package tree"', () => {
246+
const grpcPkg = {
247+
A: {
248+
C: {
249+
E: {
250+
service: {
251+
serviceName: {}
252+
}
253+
}
254+
}
255+
},
256+
B: {
257+
D: {
258+
service: {
259+
serviceName: {}
260+
}
261+
}
262+
}
263+
};
264+
const svcs = server.getServiceNames(grpcPkg);
265+
expect(svcs.length).to
266+
.be.equal(
267+
2,
268+
'Amount of services collected from namespace should be equal 2'
269+
);
270+
expect(svcs[0].name).to.be.equal('A.C.E');
271+
expect(svcs[1].name).to.be.equal('B.D');
272+
});
273+
it('should parse single level proto package tree"', () => {
274+
const grpcPkg = {
275+
A: {
276+
service: {
277+
serviceName: {}
278+
}
279+
},
280+
B: {
281+
service: {
282+
serviceName: {}
283+
}
284+
}
285+
};
286+
const services = server.getServiceNames(grpcPkg);
287+
expect(services.length).to
288+
.be.equal(
289+
2,
290+
'Amount of services collected from namespace should be equal 2'
291+
);
292+
expect(services[0].name).to.be.equal('A');
293+
expect(services[1].name).to.be.equal('B');
294+
});
295+
});
228296
});

0 commit comments

Comments
 (0)