Skip to content

Commit 535c881

Browse files
Merge branch '5.6.0' into grpc-server-max-received-and-send-message-length
2 parents 84e4bde + 986ad4e commit 535c881

6 files changed

Lines changed: 159 additions & 21 deletions

File tree

Readme.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
1717
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#8" alt="Coverage" /></a>
1818
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
19+
<a href="https://discord.gg/G7Qnnhy"><img src="https://img.shields.io/discord/520622812742811698.svg?logo=discord" alt="Discord"/></a>
1920
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
2021
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
2122
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
@@ -48,13 +49,10 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
4849

4950
<a href="https://valor-software.com/"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
5051

51-
#### Gold Sponsors
52-
53-
<a href="http://xtremis.com/"><img src="https://nestjs.com/img/logo-xtremis.svg" width="220" /></a>
54-
5552
#### Silver Sponsors
5653
<a href="https://neoteric.eu/"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" /></a> &nbsp;
57-
<a href="http://gojob.com"><img src="http://nestjs.com/img/gojob-logo.png" valign="bottom" height="95" /></a> &nbsp; <a href="https://www.swingdev.io"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="150" /> </a>
54+
<a href="http://gojob.com"><img src="http://nestjs.com/img/gojob-logo.png" valign="bottom" height="95" /></a> &nbsp; <a href="https://www.swingdev.io"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="150" /> </a> &nbsp;
55+
<a href="http://xtremis.com/"><img src="https://nestjs.com/img/logo-xtremis.svg" width="150" /></a>
5856

5957
#### Sponsors
6058

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/core/errors/messages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const INVALID_MIDDLEWARE_MESSAGE = (text, name: string) =>
5353
`The middleware doesn't provide the 'resolve' method (${name})`;
5454

5555
export const INVALID_MODULE_MESSAGE = (text, scope: string) =>
56-
`Nest cannot create the module instance. Often, this is because of a circular dependency between modules. Use forwardRef() to avoid it. (Read more https://docs.nestjs.com/advanced/circular-dependency.) Scope [${scope}]`;
56+
`Nest cannot create the module instance. Often, this is because of a circular dependency between modules. Use forwardRef() to avoid it. (Read more https://docs.nestjs.com/fundamentals/circular-dependency.) Scope [${scope}]`;
5757

5858
export const UNKNOWN_EXPORT_MESSAGE = (text, module: string) =>
5959
`Nest cannot export a component/module that is not a part of the currently processed module (${module}). Please verify whether each exported unit is available in this particular context.`;

packages/microservices/server/server-grpc.ts

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { isObject, isUndefined } from '@nestjs/common/utils/shared.utils';
12
import { fromEvent } from 'rxjs';
23
import { takeUntil } from 'rxjs/operators';
34
import {
@@ -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
}

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
});

scripts/prepare.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# 1. Install all dependencies
2-
for D in integration/*; do [ -d "${D}" ] && npm i; done
2+
for D in integration/*/; do sh -c "cd ${D} && npm i"; done
33

44
# 2. Build fresh packages and move them to sample and integration directories
55
npm run build:dev &>/dev/null

0 commit comments

Comments
 (0)