mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
samples(@nestjs) update sample applications
This commit is contained in:
@@ -18,7 +18,6 @@ describe('NATS transport', () => {
|
|||||||
app = module.createNestApplication(server);
|
app = module.createNestApplication(server);
|
||||||
app.connectMicroservice({
|
app.connectMicroservice({
|
||||||
transport: Transport.NATS,
|
transport: Transport.NATS,
|
||||||
url: 'nats://localhost:4222'
|
|
||||||
});
|
});
|
||||||
app.connectMicroservice({
|
app.connectMicroservice({
|
||||||
transport: Transport.NATS,
|
transport: Transport.NATS,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ service Math {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message SumResult {
|
message SumResult {
|
||||||
required int32 result = 1;
|
int32 result = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RequestSum {
|
message RequestSum {
|
||||||
|
|||||||
144
package-lock.json
generated
144
package-lock.json
generated
@@ -37,78 +37,6 @@
|
|||||||
"through2": "2.0.3"
|
"through2": "2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@nestjs/common": {
|
|
||||||
"version": "4.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/common/-/common-4.4.1.tgz",
|
|
||||||
"integrity": "sha1-Chu/WaCIbkqzM/i7W0v6zrECCYY=",
|
|
||||||
"requires": {
|
|
||||||
"class-transformer": "0.1.8",
|
|
||||||
"class-validator": "0.7.3",
|
|
||||||
"cli-color": "1.1.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"class-validator": {
|
|
||||||
"version": "0.7.3",
|
|
||||||
"resolved": "http://192.168.228.42:5000/class-validator/-/class-validator-0.7.3.tgz",
|
|
||||||
"integrity": "sha512-aRRlS1WlQ+52aHlmDCDX5dLwtpbg9is7i9yYhzQosTAVs86nX0Um8hb7ChTwMn7jfpyxxjAZpBrlrAc2tqNpYA==",
|
|
||||||
"requires": {
|
|
||||||
"validator": "7.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cli-color": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-3hiM3Ekp2DtnrqBBEPvtQP2/Z3U=",
|
|
||||||
"requires": {
|
|
||||||
"ansi-regex": "2.1.1",
|
|
||||||
"d": "0.1.1",
|
|
||||||
"es5-ext": "0.10.37",
|
|
||||||
"es6-iterator": "2.0.3",
|
|
||||||
"memoizee": "0.3.10",
|
|
||||||
"timers-ext": "0.1.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@nestjs/core": {
|
|
||||||
"version": "4.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-4.4.1.tgz",
|
|
||||||
"integrity": "sha1-5VHSv/dfwt4C2Ff8wTIn6HNqYVA=",
|
|
||||||
"requires": {
|
|
||||||
"body-parser": "1.17.2",
|
|
||||||
"express": "4.16.2",
|
|
||||||
"iterare": "0.0.8",
|
|
||||||
"optional": "0.1.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@nestjs/microservices": {
|
|
||||||
"version": "4.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/microservices/-/microservices-4.4.1.tgz",
|
|
||||||
"integrity": "sha1-eyjwfS6sGZZmu2tiMqErlG2YpEQ=",
|
|
||||||
"requires": {
|
|
||||||
"iterare": "0.0.8",
|
|
||||||
"json-socket": "0.2.1",
|
|
||||||
"optional": "0.1.4",
|
|
||||||
"redis": "2.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@nestjs/testing": {
|
|
||||||
"version": "4.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-4.4.1.tgz",
|
|
||||||
"integrity": "sha1-mwhdjTrZJYzwV7wbGRv/4EhPQps=",
|
|
||||||
"requires": {
|
|
||||||
"optional": "0.1.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@nestjs/websockets": {
|
|
||||||
"version": "4.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-4.4.1.tgz",
|
|
||||||
"integrity": "sha1-uvxDQQijIGY3Vqy3mgZ/lgIGnMk=",
|
|
||||||
"requires": {
|
|
||||||
"iterare": "0.0.8",
|
|
||||||
"socket.io": "2.0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@protobufjs/aspromise": {
|
"@protobufjs/aspromise": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
@@ -1887,6 +1815,7 @@
|
|||||||
"version": "1.17.2",
|
"version": "1.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.17.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.17.2.tgz",
|
||||||
"integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=",
|
"integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bytes": "2.4.0",
|
"bytes": "2.4.0",
|
||||||
"content-type": "1.0.4",
|
"content-type": "1.0.4",
|
||||||
@@ -2068,7 +1997,8 @@
|
|||||||
"bytes": {
|
"bytes": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz",
|
||||||
"integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk="
|
"integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"cache-base": {
|
"cache-base": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@@ -3427,14 +3357,6 @@
|
|||||||
"array-find-index": "1.0.2"
|
"array-find-index": "1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"d": {
|
|
||||||
"version": "0.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz",
|
|
||||||
"integrity": "sha1-2hhMU10Y2O57oqoim5FACfrhEwk=",
|
|
||||||
"requires": {
|
|
||||||
"es5-ext": "0.10.37"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dargs": {
|
"dargs": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz",
|
||||||
@@ -4052,38 +3974,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"es6-weak-map": {
|
|
||||||
"version": "0.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-0.1.4.tgz",
|
|
||||||
"integrity": "sha1-cGzvnpmqI2undmwjnIueKG6n0ig=",
|
|
||||||
"requires": {
|
|
||||||
"d": "0.1.1",
|
|
||||||
"es5-ext": "0.10.37",
|
|
||||||
"es6-iterator": "0.1.3",
|
|
||||||
"es6-symbol": "2.0.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"es6-iterator": {
|
|
||||||
"version": "0.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-0.1.3.tgz",
|
|
||||||
"integrity": "sha1-1vWLjE/EE8JJtLqhl2j45NfIlE4=",
|
|
||||||
"requires": {
|
|
||||||
"d": "0.1.1",
|
|
||||||
"es5-ext": "0.10.37",
|
|
||||||
"es6-symbol": "2.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"es6-symbol": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-2.0.1.tgz",
|
|
||||||
"integrity": "sha1-dhtcZ8/U8dGK+yNPaR1nhoLLO/M=",
|
|
||||||
"requires": {
|
|
||||||
"d": "0.1.1",
|
|
||||||
"es5-ext": "0.10.37"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"escape-html": {
|
"escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
@@ -9527,20 +9417,6 @@
|
|||||||
"mimic-fn": "1.1.0"
|
"mimic-fn": "1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"memoizee": {
|
|
||||||
"version": "0.3.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.3.10.tgz",
|
|
||||||
"integrity": "sha1-TsoNiu057J0Bf0xcLy9kMvQuXI8=",
|
|
||||||
"requires": {
|
|
||||||
"d": "0.1.1",
|
|
||||||
"es5-ext": "0.10.37",
|
|
||||||
"es6-weak-map": "0.1.4",
|
|
||||||
"event-emitter": "0.3.5",
|
|
||||||
"lru-queue": "0.1.0",
|
|
||||||
"next-tick": "0.2.2",
|
|
||||||
"timers-ext": "0.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"memory-fs": {
|
"memory-fs": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
||||||
@@ -9978,11 +9854,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
||||||
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
|
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
|
||||||
},
|
},
|
||||||
"next-tick": {
|
|
||||||
"version": "0.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-0.2.2.tgz",
|
|
||||||
"integrity": "sha1-ddpKkn7liH45BliABltzNkE7MQ0="
|
|
||||||
},
|
|
||||||
"node-dir": {
|
"node-dir": {
|
||||||
"version": "0.1.8",
|
"version": "0.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.8.tgz",
|
||||||
@@ -12497,7 +12368,8 @@
|
|||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
|
||||||
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
|
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"quick-format-unescaped": {
|
"quick-format-unescaped": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
@@ -12541,6 +12413,7 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz",
|
||||||
"integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=",
|
"integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"bytes": "2.4.0",
|
"bytes": "2.4.0",
|
||||||
"iconv-lite": "0.4.15",
|
"iconv-lite": "0.4.15",
|
||||||
@@ -14777,11 +14650,6 @@
|
|||||||
"spdx-expression-parse": "1.0.4"
|
"spdx-expression-parse": "1.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"validator": {
|
|
||||||
"version": "7.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/validator/-/validator-7.2.0.tgz",
|
|
||||||
"integrity": "sha1-pj3Lq6UdQ1C/jfIJiODVpU1xF5E="
|
|
||||||
},
|
|
||||||
"vary": {
|
"vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -24,11 +24,11 @@
|
|||||||
"author": "Kamil Mysliwiec",
|
"author": "Kamil Mysliwiec",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^4.0.0",
|
"@nestjs/common": "^5.0.0",
|
||||||
"@nestjs/core": "^4.0.0",
|
"@nestjs/core": "^5.0.0",
|
||||||
"@nestjs/microservices": "^4.0.0",
|
"@nestjs/microservices": "^5.0.0",
|
||||||
"@nestjs/testing": "^4.0.0",
|
"@nestjs/testing": "^5.0.0",
|
||||||
"@nestjs/websockets": "^4.0.0",
|
"@nestjs/websockets": "^5.0.0",
|
||||||
"axios": "^0.17.1",
|
"axios": "^0.17.1",
|
||||||
"class-transformer": "^0.1.8",
|
"class-transformer": "^0.1.8",
|
||||||
"class-validator": "^0.8.1",
|
"class-validator": "^0.8.1",
|
||||||
|
|||||||
@@ -53,6 +53,6 @@ export function mixin(mixinClass) {
|
|||||||
Object.defineProperty(mixinClass, 'name', {
|
Object.defineProperty(mixinClass, 'name', {
|
||||||
value: JSON.stringify(this.offset),
|
value: JSON.stringify(this.offset),
|
||||||
});
|
});
|
||||||
Component()(mixinClass);
|
Injectable()(mixinClass);
|
||||||
return mixinClass;
|
return mixinClass;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,14 @@ import { EXCEPTION_FILTERS_METADATA } from '../../constants';
|
|||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
import { ExceptionFilter } from '../../index';
|
import { ExceptionFilter } from '../../index';
|
||||||
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
||||||
|
import { isFunction } from '../../utils/shared.utils';
|
||||||
|
import { validateEach } from '../../utils/validate-each.util';
|
||||||
|
|
||||||
const defineFiltersMetadata = (...filters: ExceptionFilter[]) => {
|
const defineFiltersMetadata = (...filters: ExceptionFilter[]) => {
|
||||||
return (target: object, key?, descriptor?) => {
|
return (target: any, key?, descriptor?) => {
|
||||||
|
const isFilterValid = (filter) => isFunction(filter.catch);
|
||||||
if (descriptor) {
|
if (descriptor) {
|
||||||
|
validateEach(target.constructor, filters, isFilterValid, '@UseFilters', 'filter');
|
||||||
extendArrayMetadata(
|
extendArrayMetadata(
|
||||||
EXCEPTION_FILTERS_METADATA,
|
EXCEPTION_FILTERS_METADATA,
|
||||||
filters,
|
filters,
|
||||||
@@ -14,6 +18,7 @@ const defineFiltersMetadata = (...filters: ExceptionFilter[]) => {
|
|||||||
);
|
);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
validateEach(target, filters, isFilterValid, '@UseFilters', 'filter');
|
||||||
extendArrayMetadata(EXCEPTION_FILTERS_METADATA, filters, target);
|
extendArrayMetadata(EXCEPTION_FILTERS_METADATA, filters, target);
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { GUARDS_METADATA } from '../../constants';
|
import { GUARDS_METADATA } from '../../constants';
|
||||||
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
||||||
|
import { validateEach } from '../../utils/validate-each.util';
|
||||||
|
import { isFunction } from '../../utils/shared.utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds guards to the particular context.
|
* Binds guards to the particular context.
|
||||||
@@ -12,11 +14,13 @@ import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
|||||||
* @param {} ...guards (types)
|
* @param {} ...guards (types)
|
||||||
*/
|
*/
|
||||||
export function UseGuards(...guards: any[]) {
|
export function UseGuards(...guards: any[]) {
|
||||||
return (target: object, key?, descriptor?) => {
|
return (target: any, key?, descriptor?) => {
|
||||||
if (descriptor) {
|
if (descriptor) {
|
||||||
|
validateEach(target.constructor, guards, isFunction, '@UseGuards', 'guard');
|
||||||
extendArrayMetadata(GUARDS_METADATA, guards, descriptor.value);
|
extendArrayMetadata(GUARDS_METADATA, guards, descriptor.value);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
validateEach(target, guards, isFunction, '@UseGuards', 'guard');
|
||||||
extendArrayMetadata(GUARDS_METADATA, guards, target);
|
extendArrayMetadata(GUARDS_METADATA, guards, target);
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { INTERCEPTORS_METADATA } from '../../constants';
|
import { INTERCEPTORS_METADATA } from '../../constants';
|
||||||
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
||||||
|
import { isFunction } from '../../utils/shared.utils';
|
||||||
|
import { validateEach } from '../../utils/validate-each.util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds interceptors to the particular context.
|
* Binds interceptors to the particular context.
|
||||||
@@ -12,8 +14,15 @@ import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
|||||||
* @param {} ...interceptors (types)
|
* @param {} ...interceptors (types)
|
||||||
*/
|
*/
|
||||||
export function UseInterceptors(...interceptors: any[]) {
|
export function UseInterceptors(...interceptors: any[]) {
|
||||||
return (target: object, key?, descriptor?) => {
|
return (target: any, key?, descriptor?) => {
|
||||||
if (descriptor) {
|
if (descriptor) {
|
||||||
|
validateEach(
|
||||||
|
target.constructor,
|
||||||
|
interceptors,
|
||||||
|
isFunction,
|
||||||
|
'@UseInterceptors',
|
||||||
|
'interceptor',
|
||||||
|
);
|
||||||
extendArrayMetadata(
|
extendArrayMetadata(
|
||||||
INTERCEPTORS_METADATA,
|
INTERCEPTORS_METADATA,
|
||||||
interceptors,
|
interceptors,
|
||||||
@@ -21,6 +30,13 @@ export function UseInterceptors(...interceptors: any[]) {
|
|||||||
);
|
);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
validateEach(
|
||||||
|
target,
|
||||||
|
interceptors,
|
||||||
|
isFunction,
|
||||||
|
'@UseInterceptors',
|
||||||
|
'interceptor',
|
||||||
|
);
|
||||||
extendArrayMetadata(INTERCEPTORS_METADATA, interceptors, target);
|
extendArrayMetadata(INTERCEPTORS_METADATA, interceptors, target);
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { PipeTransform } from '../../interfaces/index';
|
import { PipeTransform } from '../../interfaces/index';
|
||||||
import { PIPES_METADATA } from '../../constants';
|
import { PIPES_METADATA } from '../../constants';
|
||||||
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
||||||
|
import { validateEach } from '../../utils/validate-each.util';
|
||||||
|
import { isFunction } from '../../utils/shared.utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds pipes to the particular context.
|
* Binds pipes to the particular context.
|
||||||
@@ -13,11 +15,14 @@ import { extendArrayMetadata } from '../../utils/extend-metadata.util';
|
|||||||
* @param {PipeTransform[]} ...pipes (instances)
|
* @param {PipeTransform[]} ...pipes (instances)
|
||||||
*/
|
*/
|
||||||
export function UsePipes(...pipes: PipeTransform<any>[]) {
|
export function UsePipes(...pipes: PipeTransform<any>[]) {
|
||||||
return (target: object, key?, descriptor?) => {
|
return (target: any, key?, descriptor?) => {
|
||||||
|
const isPipeValid = (pipe) => isFunction(pipe.transform);
|
||||||
if (descriptor) {
|
if (descriptor) {
|
||||||
|
validateEach(target.constructor, pipes, isPipeValid, '@UsePipes', 'pipe');
|
||||||
extendArrayMetadata(PIPES_METADATA, pipes, descriptor.value);
|
extendArrayMetadata(PIPES_METADATA, pipes, descriptor.value);
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
validateEach(target, pipes, isPipeValid, '@UsePipes', 'pipe');
|
||||||
extendArrayMetadata(PIPES_METADATA, pipes, target);
|
extendArrayMetadata(PIPES_METADATA, pipes, target);
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,46 +3,64 @@ import { CustomTransportStrategy } from './custom-transport-strategy.interface';
|
|||||||
import { IClientOptions } from 'mqtt';
|
import { IClientOptions } from 'mqtt';
|
||||||
import { ServerCredentials } from 'grpc';
|
import { ServerCredentials } from 'grpc';
|
||||||
|
|
||||||
export interface MicroserviceOptions {
|
export type MicroserviceOptions =
|
||||||
transport?: Transport;
|
| GrpcOptions
|
||||||
|
| TcpOptions
|
||||||
|
| RedisOptions
|
||||||
|
| NatsOptions
|
||||||
|
| MqttOptions
|
||||||
|
| CustomStrategy;
|
||||||
|
|
||||||
|
export interface CustomStrategy {
|
||||||
strategy?: CustomTransportStrategy;
|
strategy?: CustomTransportStrategy;
|
||||||
options?:
|
options?: {};
|
||||||
| TcpOptions
|
|
||||||
| RedisOptions
|
|
||||||
| NatsOptions
|
|
||||||
| MqttOptions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GrpcOptions {
|
export interface GrpcOptions {
|
||||||
url?: string;
|
transport?: Transport.GRPC;
|
||||||
credentials?: ServerCredentials;
|
options: {
|
||||||
protoPath: string;
|
url?: string;
|
||||||
package: string;
|
credentials?: ServerCredentials;
|
||||||
|
protoPath: string;
|
||||||
|
package: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TcpOptions {
|
export interface TcpOptions {
|
||||||
host?: string;
|
transport?: Transport.TCP;
|
||||||
port?: number;
|
options?: {
|
||||||
retryAttempts?: number;
|
host?: string;
|
||||||
retryDelay?: number;
|
port?: number;
|
||||||
|
retryAttempts?: number;
|
||||||
|
retryDelay?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RedisOptions {
|
export interface RedisOptions {
|
||||||
url?: string;
|
transport?: Transport.REDIS;
|
||||||
retryAttempts?: number;
|
options?: {
|
||||||
retryDelay?: number;
|
url?: string;
|
||||||
|
retryAttempts?: number;
|
||||||
|
retryDelay?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MqttOptions extends IClientOptions {
|
export interface MqttOptions {
|
||||||
url?: string;
|
transport?: Transport.MQTT;
|
||||||
|
options?: IClientOptions & {
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NatsOptions {
|
export interface NatsOptions {
|
||||||
url?: string;
|
transport?: Transport.NATS;
|
||||||
name?: string;
|
options?: {
|
||||||
pass?: string;
|
url?: string;
|
||||||
maxReconnectAttempts?: number;
|
name?: string;
|
||||||
reconnectTimeWait?: number;
|
pass?: string;
|
||||||
servers?: string[];
|
maxReconnectAttempts?: number;
|
||||||
tls?: any;
|
reconnectTimeWait?: number;
|
||||||
}
|
servers?: string[];
|
||||||
|
tls?: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,5 +3,4 @@ import { LoggerService } from '../../services/logger.service';
|
|||||||
import { NestApplicationContextOptions } from '../nest-application-context-options.interface';
|
import { NestApplicationContextOptions } from '../nest-application-context-options.interface';
|
||||||
|
|
||||||
export interface NestMicroserviceOptions
|
export interface NestMicroserviceOptions
|
||||||
extends MicroserviceOptions,
|
extends NestApplicationContextOptions {}
|
||||||
NestApplicationContextOptions {}
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { MiddlewaresConsumer } from './middlewares-consumer.interface';
|
import { MiddlewaresConsumer } from './middlewares-consumer.interface';
|
||||||
import { RequestMappingMetadata } from '../request-mapping-metadata.interface';
|
import { RequestMappingMetadata } from '../request-mapping-metadata.interface';
|
||||||
|
import { Type } from '../type.interface';
|
||||||
|
|
||||||
export interface MiddlewareConfigProxy {
|
export interface MiddlewareConfigProxy {
|
||||||
/**
|
/**
|
||||||
@@ -8,20 +9,14 @@ export interface MiddlewareConfigProxy {
|
|||||||
* @param {} ...data
|
* @param {} ...data
|
||||||
* @returns MiddlewareConfigProxy
|
* @returns MiddlewareConfigProxy
|
||||||
*/
|
*/
|
||||||
with(...data): MiddlewareConfigProxy;
|
with(...data: any[]): MiddlewareConfigProxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches passed routes / controllers to the processed middleware(s).
|
* Attaches passed either routes (strings) or controllers to the processed middleware(s).
|
||||||
* Single route can be defined as a literal object:
|
|
||||||
* ```
|
|
||||||
* path: string;
|
|
||||||
* method: RequestMethod;
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* When you pass Controller class, Nest will attach middleware to every HTTP route handler inside this controller.
|
* When you pass Controller class, Nest will attach middleware to every HTTP route handler inside this controller.
|
||||||
*
|
*
|
||||||
* @param {} ...routes
|
* @param {} ...routes
|
||||||
* @returns MiddlewaresConsumer
|
* @returns MiddlewaresConsumer
|
||||||
*/
|
*/
|
||||||
forRoutes(...routes): MiddlewaresConsumer;
|
forRoutes(...routes: (string | Type<any>)[]): MiddlewaresConsumer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { MiddlewareConfigProxy } from './middleware-config-proxy.interface';
|
|||||||
|
|
||||||
export interface MiddlewaresConsumer {
|
export interface MiddlewaresConsumer {
|
||||||
/**
|
/**
|
||||||
* Takes single middleware class or array of classes,
|
* Takes single middleware class or array of classes
|
||||||
* which subsequently can be attached to the passed routes / controllers.
|
* that subsequently could be attached to the passed either routes or controllers.
|
||||||
*
|
*
|
||||||
* @param {any|any[]} middlewares
|
* @param {any|any[]} middlewares
|
||||||
* @returns MiddlewareConfigProxy
|
* @returns MiddlewareConfigProxy
|
||||||
|
|||||||
@@ -19,5 +19,5 @@ export interface ValueProvider {
|
|||||||
export interface FactoryProvider {
|
export interface FactoryProvider {
|
||||||
provide: any;
|
provide: any;
|
||||||
useFactory: (...args: any[]) => any;
|
useFactory: (...args: any[]) => any;
|
||||||
inject: Array<Type<any> | string | any>;
|
inject?: Array<Type<any> | string | any>;
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import { CanActivate } from './features/can-activate.interface';
|
|||||||
import { NestInterceptor } from './features/nest-interceptor.interface';
|
import { NestInterceptor } from './features/nest-interceptor.interface';
|
||||||
import { INestApplicationContext } from './nest-application-context.interface';
|
import { INestApplicationContext } from './nest-application-context.interface';
|
||||||
import { CorsOptions } from './external/cors-options.interface';
|
import { CorsOptions } from './external/cors-options.interface';
|
||||||
|
import { MicroserviceOptions } from './microservices/microservice-configuration.interface';
|
||||||
|
|
||||||
export interface INestApplication extends INestApplicationContext {
|
export interface INestApplication extends INestApplicationContext {
|
||||||
/**
|
/**
|
||||||
@@ -70,12 +71,12 @@ export interface INestApplication extends INestApplicationContext {
|
|||||||
useWebSocketAdapter(adapter: WebSocketAdapter): this;
|
useWebSocketAdapter(adapter: WebSocketAdapter): this;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects microservice to the NestApplication instance. It transforms application to the hybrid instance.
|
* Connects microservice to the NestApplication instance. Transforms application to the hybrid instance.
|
||||||
*
|
*
|
||||||
* @param {MicroserviceConfiguration} config Microservice configuration objet
|
* @param {MicroserviceOptions} options Microservice options object
|
||||||
* @returns INestMicroservice
|
* @returns INestMicroservice
|
||||||
*/
|
*/
|
||||||
connectMicroservice(config): INestMicroservice;
|
connectMicroservice(options: MicroserviceOptions): INestMicroservice;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns array of the connected microservices to the NestApplication.
|
* Returns array of the connected microservices to the NestApplication.
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ export interface INestExpressApplication {
|
|||||||
*
|
*
|
||||||
* @returns this
|
* @returns this
|
||||||
*/
|
*/
|
||||||
useStaticAssets(path: string, options: ServeStaticOptions): this;
|
useStaticAssets(options: any): this;
|
||||||
|
useStaticAssets(path: string, options?: ServeStaticOptions);
|
||||||
|
useStaticAssets(pathOrOptions: any, options?: ServeStaticOptions): this;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a base directory for templates (views).
|
* Sets a base directory for templates (views).
|
||||||
|
|||||||
@@ -2,9 +2,14 @@ import 'reflect-metadata';
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { EXCEPTION_FILTERS_METADATA } from '../../constants';
|
import { EXCEPTION_FILTERS_METADATA } from '../../constants';
|
||||||
import { UseFilters } from '../../decorators/core/exception-filters.decorator';
|
import { UseFilters } from '../../decorators/core/exception-filters.decorator';
|
||||||
|
import { InvalidDecoratorItemException } from '../../utils/validate-each.util';
|
||||||
|
|
||||||
|
class Filter {
|
||||||
|
catch() {}
|
||||||
|
}
|
||||||
|
|
||||||
describe('@UseFilters', () => {
|
describe('@UseFilters', () => {
|
||||||
const filters = ['exception', 'exception2'];
|
const filters = [new Filter(), new Filter()];
|
||||||
|
|
||||||
@UseFilters(...(filters as any))
|
@UseFilters(...(filters as any))
|
||||||
class Test {}
|
class Test {}
|
||||||
@@ -26,4 +31,13 @@ describe('@UseFilters', () => {
|
|||||||
);
|
);
|
||||||
expect(metadata).to.be.eql(filters);
|
expect(metadata).to.be.eql(filters);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('when object is invalid should throw exception', () => {
|
||||||
|
try {
|
||||||
|
UseFilters('test' as any)({});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
expect(e).to.be.instanceof(InvalidDecoratorItemException);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import 'reflect-metadata';
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { UseGuards } from '../../decorators/core/use-guards.decorator';
|
import { UseGuards } from '../../decorators/core/use-guards.decorator';
|
||||||
import { GUARDS_METADATA } from '../../constants';
|
import { GUARDS_METADATA } from '../../constants';
|
||||||
|
import { InvalidDecoratorItemException } from '../../utils/validate-each.util';
|
||||||
|
|
||||||
|
class Guard {}
|
||||||
|
|
||||||
describe('@UseGuards', () => {
|
describe('@UseGuards', () => {
|
||||||
const guards = ['guard1', 'guard2'];
|
const guards = [Guard, Guard];
|
||||||
|
|
||||||
@UseGuards(...(guards as any))
|
@UseGuards(...(guards as any))
|
||||||
class Test {}
|
class Test {}
|
||||||
@@ -34,4 +37,13 @@ describe('@UseGuards', () => {
|
|||||||
const metadata = Reflect.getMetadata(GUARDS_METADATA, Test2.test);
|
const metadata = Reflect.getMetadata(GUARDS_METADATA, Test2.test);
|
||||||
expect(metadata).to.be.eql(guards.concat(guards));
|
expect(metadata).to.be.eql(guards.concat(guards));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('when object is invalid should throw exception', () => {
|
||||||
|
try {
|
||||||
|
UseGuards('test')({});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
expect(e).to.be.instanceof(InvalidDecoratorItemException);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import 'reflect-metadata';
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { UseInterceptors } from '../../decorators/core/use-interceptors.decorator';
|
import { UseInterceptors } from '../../decorators/core/use-interceptors.decorator';
|
||||||
import { INTERCEPTORS_METADATA } from '../../constants';
|
import { INTERCEPTORS_METADATA } from '../../constants';
|
||||||
|
import { InvalidDecoratorItemException } from '../../utils/validate-each.util';
|
||||||
|
|
||||||
|
class Interceptor {}
|
||||||
|
|
||||||
describe('@UseInterceptors', () => {
|
describe('@UseInterceptors', () => {
|
||||||
const interceptors = ['interceptor1', 'interceptor2'];
|
const interceptors = [Interceptor, Interceptor];
|
||||||
|
|
||||||
@UseInterceptors(...(interceptors as any))
|
@UseInterceptors(...(interceptors as any))
|
||||||
class Test {}
|
class Test {}
|
||||||
@@ -26,4 +29,13 @@ describe('@UseInterceptors', () => {
|
|||||||
);
|
);
|
||||||
expect(metadata).to.be.eql(interceptors);
|
expect(metadata).to.be.eql(interceptors);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('when object is invalid should throw exception', () => {
|
||||||
|
try {
|
||||||
|
UseInterceptors('test')({});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
expect(e).to.be.instanceof(InvalidDecoratorItemException);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,14 @@ import 'reflect-metadata';
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { UsePipes } from '../../decorators/core/use-pipes.decorator';
|
import { UsePipes } from '../../decorators/core/use-pipes.decorator';
|
||||||
import { PIPES_METADATA } from '../../constants';
|
import { PIPES_METADATA } from '../../constants';
|
||||||
|
import { InvalidDecoratorItemException } from '../../utils/validate-each.util';
|
||||||
|
|
||||||
|
class Pipe {
|
||||||
|
transform() {}
|
||||||
|
}
|
||||||
|
|
||||||
describe('@UsePipes', () => {
|
describe('@UsePipes', () => {
|
||||||
const pipes = ['pipe1', 'pipe2'];
|
const pipes = [new Pipe(), new Pipe()];
|
||||||
|
|
||||||
@UsePipes(...(pipes as any))
|
@UsePipes(...(pipes as any))
|
||||||
class Test {}
|
class Test {}
|
||||||
@@ -23,4 +28,13 @@ describe('@UsePipes', () => {
|
|||||||
const metadata = Reflect.getMetadata(PIPES_METADATA, TestWithMethod.test);
|
const metadata = Reflect.getMetadata(PIPES_METADATA, TestWithMethod.test);
|
||||||
expect(metadata).to.be.eql(pipes);
|
expect(metadata).to.be.eql(pipes);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('when object is invalid should throw exception', () => {
|
||||||
|
try {
|
||||||
|
UsePipes('test' as any)({});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
expect(e).to.be.instanceof(InvalidDecoratorItemException);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
23
packages/common/test/utils/validate-each.util.spec.ts
Normal file
23
packages/common/test/utils/validate-each.util.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { expect } from 'chai';
|
||||||
|
import { isFunction } from '../../utils/shared.utils';
|
||||||
|
import { validateEach, InvalidDecoratorItemException } from '../../utils/validate-each.util';
|
||||||
|
|
||||||
|
describe('validateEach', () => {
|
||||||
|
describe('when any item will not pass predicate', () => {
|
||||||
|
it('should throw exception', () => {
|
||||||
|
try {
|
||||||
|
validateEach({} as any, ['test'], isFunction, '', '');
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
expect(e).to.be.instanceof(InvalidDecoratorItemException);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
describe('when all items passed predicate', () => {
|
||||||
|
it('should return true', () => {
|
||||||
|
expect(
|
||||||
|
validateEach({} as any, [() => null], isFunction, '', '')
|
||||||
|
).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
24
packages/common/utils/validate-each.util.ts
Normal file
24
packages/common/utils/validate-each.util.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
|
||||||
|
|
||||||
|
export class InvalidDecoratorItemException extends RuntimeException {
|
||||||
|
constructor(decorator: string, item: string, context: string) {
|
||||||
|
super(`Invalid ${item} passed to ${decorator}() decorator (${context}).`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateEach(
|
||||||
|
context: { name: string },
|
||||||
|
arr: any[],
|
||||||
|
predicate: Function,
|
||||||
|
decorator: string,
|
||||||
|
item: string,
|
||||||
|
): boolean {
|
||||||
|
if (!context || !context.name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const errors = arr.filter(item => !predicate(item));
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw new InvalidDecoratorItemException(decorator, item, context.name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -10,12 +10,8 @@ import { ServeStaticOptions } from '@nestjs/common/interfaces/external/serve-sta
|
|||||||
export class ExpressAdapter implements HttpServer {
|
export class ExpressAdapter implements HttpServer {
|
||||||
constructor(private readonly instance) {}
|
constructor(private readonly instance) {}
|
||||||
|
|
||||||
use(handler: RequestHandler | ErrorHandler);
|
use(...args: any[]) {
|
||||||
use(path: any, handler: RequestHandler | ErrorHandler);
|
return this.instance.use(...args);
|
||||||
use(pathOrHandler: any, handler?: RequestHandler | ErrorHandler | undefined) {
|
|
||||||
return handler
|
|
||||||
? this.instance.use(pathOrHandler, handler)
|
|
||||||
: this.instance.use(pathOrHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get(handler: RequestHandler);
|
get(handler: RequestHandler);
|
||||||
|
|||||||
1
packages/core/adapters/index.ts
Normal file
1
packages/core/adapters/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './fastify-adapter';
|
||||||
@@ -24,7 +24,7 @@ export const UnknownExportMessage = (module: string) =>
|
|||||||
`Nest cannot export component / module that is not a part of the currently proccessed module (${module}). Please verify whether each exported unit is available in this particular context.`;
|
`Nest cannot export component / module that is not a part of the currently proccessed module (${module}). Please verify whether each exported unit is available in this particular context.`;
|
||||||
|
|
||||||
export const MissingRequiredDependency = (name: string, reason: string) =>
|
export const MissingRequiredDependency = (name: string, reason: string) =>
|
||||||
`The ${name} package is missing. Please, make sure to install it (npm install ${name}) to take advantage of ${reason}.`
|
`The "${name}" package is missing. Please, make sure to install this library (npm install ${name}) to take advantage of ${reason} class.`
|
||||||
|
|
||||||
export const INVALID_MIDDLEWARE_CONFIGURATION = `Invalid middleware configuration passed inside the module 'configure()' method.`;
|
export const INVALID_MIDDLEWARE_CONFIGURATION = `Invalid middleware configuration passed inside the module 'configure()' method.`;
|
||||||
export const UNKNOWN_REQUEST_MAPPING = `Request mapping properties not defined in the @RequestMapping() annotation!`;
|
export const UNKNOWN_REQUEST_MAPPING = `Request mapping properties not defined in the @RequestMapping() annotation!`;
|
||||||
|
|||||||
@@ -2,10 +2,15 @@ import 'reflect-metadata';
|
|||||||
import { RouterExplorer } from '../router/router-explorer';
|
import { RouterExplorer } from '../router/router-explorer';
|
||||||
import { UnknownRequestMappingException } from '../errors/exceptions/unknown-request-mapping.exception';
|
import { UnknownRequestMappingException } from '../errors/exceptions/unknown-request-mapping.exception';
|
||||||
import { RequestMethod } from '@nestjs/common/enums/request-method.enum';
|
import { RequestMethod } from '@nestjs/common/enums/request-method.enum';
|
||||||
import { isUndefined, validatePath, isString } from '@nestjs/common/utils/shared.utils';
|
import {
|
||||||
|
isUndefined,
|
||||||
|
validatePath,
|
||||||
|
isString,
|
||||||
|
} from '@nestjs/common/utils/shared.utils';
|
||||||
import { PATH_METADATA } from '@nestjs/common/constants';
|
import { PATH_METADATA } from '@nestjs/common/constants';
|
||||||
import { MetadataScanner } from '../metadata-scanner';
|
import { MetadataScanner } from '../metadata-scanner';
|
||||||
import { NestContainer } from '../injector/container';
|
import { NestContainer } from '../injector/container';
|
||||||
|
import { Type } from '@nestjs/common/interfaces';
|
||||||
|
|
||||||
export class RoutesMapper {
|
export class RoutesMapper {
|
||||||
private readonly routerExplorer: RouterExplorer;
|
private readonly routerExplorer: RouterExplorer;
|
||||||
@@ -14,18 +19,25 @@ export class RoutesMapper {
|
|||||||
this.routerExplorer = new RouterExplorer(new MetadataScanner(), container);
|
this.routerExplorer = new RouterExplorer(new MetadataScanner(), container);
|
||||||
}
|
}
|
||||||
|
|
||||||
public mapRouteToRouteProps(routeMetatype): string[] {
|
public mapRouteToRouteProps(route: Type<any> | any | string): string[] {
|
||||||
const routePath: string = Reflect.getMetadata(PATH_METADATA, routeMetatype);
|
if (isString(route)) {
|
||||||
|
return [route];
|
||||||
|
}
|
||||||
|
const routePath: string = Reflect.getMetadata(PATH_METADATA, route);
|
||||||
if (isUndefined(routePath)) {
|
if (isUndefined(routePath)) {
|
||||||
return [this.mapObjectToPath(routeMetatype)];
|
return [this.mapObjectToPath(route)];
|
||||||
}
|
}
|
||||||
const paths = this.routerExplorer.scanForPaths(
|
const paths = this.routerExplorer.scanForPaths(
|
||||||
Object.create(routeMetatype),
|
Object.create(route),
|
||||||
routeMetatype.prototype,
|
route.prototype,
|
||||||
|
);
|
||||||
|
const uniquePathsSet = new Set(
|
||||||
|
paths.map(
|
||||||
|
route =>
|
||||||
|
this.validateGlobalPath(routePath) +
|
||||||
|
this.validateRoutePath(route.path),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
const uniquePathsSet = new Set(paths.map(route => (
|
|
||||||
this.validateGlobalPath(routePath) + this.validateRoutePath(route.path)
|
|
||||||
)));
|
|
||||||
return [...uniquePathsSet.values()];
|
return [...uniquePathsSet.values()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import * as http from 'http';
|
|||||||
import * as https from 'https';
|
import * as https from 'https';
|
||||||
import * as optional from 'optional';
|
import * as optional from 'optional';
|
||||||
import * as bodyParser from 'body-parser';
|
import * as bodyParser from 'body-parser';
|
||||||
import * as formbody from 'fastify-formbody';
|
|
||||||
import iterate from 'iterare';
|
import iterate from 'iterare';
|
||||||
import {
|
import {
|
||||||
CanActivate,
|
CanActivate,
|
||||||
@@ -46,6 +45,7 @@ import { FastifyAdapter } from './adapters/fastify-adapter';
|
|||||||
import { INestExpressApplication } from '@nestjs/common/interfaces/nest-express-application.interface';
|
import { INestExpressApplication } from '@nestjs/common/interfaces/nest-express-application.interface';
|
||||||
import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface';
|
import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface';
|
||||||
import { ServeStaticOptions } from '@nestjs/common/interfaces/external/serve-static-options.interface';
|
import { ServeStaticOptions } from '@nestjs/common/interfaces/external/serve-static-options.interface';
|
||||||
|
import { MissingRequiredDependencyException } from './errors/exceptions/missing-dependency.exception';
|
||||||
|
|
||||||
const { SocketModule } =
|
const { SocketModule } =
|
||||||
optional('@nestjs/websockets/socket-module') || ({} as any);
|
optional('@nestjs/websockets/socket-module') || ({} as any);
|
||||||
@@ -159,7 +159,9 @@ export class NestApplication extends NestApplicationContext
|
|||||||
|
|
||||||
public registerParserMiddlewares() {
|
public registerParserMiddlewares() {
|
||||||
if (this.httpAdapter instanceof FastifyAdapter) {
|
if (this.httpAdapter instanceof FastifyAdapter) {
|
||||||
return this.httpAdapter.register(formbody);
|
return this.httpAdapter.register(
|
||||||
|
this.loadPackage('fastify-formbody', 'FastifyAdapter'),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!this.isExpress()) {
|
if (!this.isExpress()) {
|
||||||
return void 0;
|
return void 0;
|
||||||
@@ -341,25 +343,33 @@ export class NestApplication extends NestApplicationContext
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
useStaticAssets(options: any): this;
|
public useStaticAssets(options: any): this;
|
||||||
useStaticAssets(path: string, options?: ServeStaticOptions);
|
public useStaticAssets(path: string, options?: ServeStaticOptions);
|
||||||
useStaticAssets(pathOrOptions: any, options?: ServeStaticOptions): this {
|
public useStaticAssets(pathOrOptions: any, options?: ServeStaticOptions): this {
|
||||||
this.httpAdapter.useStaticAssets &&
|
this.httpAdapter.useStaticAssets &&
|
||||||
this.httpAdapter.useStaticAssets(pathOrOptions, options);
|
this.httpAdapter.useStaticAssets(pathOrOptions, options);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setBaseViewsDir(path: string): this {
|
public setBaseViewsDir(path: string): this {
|
||||||
this.httpAdapter.setBaseViewsDir && this.httpAdapter.setBaseViewsDir(path);
|
this.httpAdapter.setBaseViewsDir && this.httpAdapter.setBaseViewsDir(path);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setViewEngine(engineOrOptions: any): this {
|
public setViewEngine(engineOrOptions: any): this {
|
||||||
this.httpAdapter.setViewEngine &&
|
this.httpAdapter.setViewEngine &&
|
||||||
this.httpAdapter.setViewEngine(engineOrOptions);
|
this.httpAdapter.setViewEngine(engineOrOptions);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadPackage(name: string, ctx: string) {
|
||||||
|
try {
|
||||||
|
return require(name);
|
||||||
|
} catch (e) {
|
||||||
|
throw new MissingRequiredDependencyException(name, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async registerMiddlewares(instance) {
|
private async registerMiddlewares(instance) {
|
||||||
await this.middlewaresModule.registerMiddlewares(
|
await this.middlewaresModule.registerMiddlewares(
|
||||||
this.middlewaresContainer,
|
this.middlewaresContainer,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { ExpressAdapter } from './adapters/express-adapter';
|
|||||||
import { INestExpressApplication } from '@nestjs/common/interfaces/nest-express-application.interface';
|
import { INestExpressApplication } from '@nestjs/common/interfaces/nest-express-application.interface';
|
||||||
import { FastifyAdapter } from './adapters/fastify-adapter';
|
import { FastifyAdapter } from './adapters/fastify-adapter';
|
||||||
import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface';
|
import { INestFastifyApplication } from '@nestjs/common/interfaces/nest-fastify-application.interface';
|
||||||
|
import { MicroserviceOptions } from '@nestjs/common/interfaces/microservices/microservice-configuration.interface';
|
||||||
|
|
||||||
const { NestMicroservice } =
|
const { NestMicroservice } =
|
||||||
optional('@nestjs/microservices/nest-microservice') || ({} as any);
|
optional('@nestjs/microservices/nest-microservice') || ({} as any);
|
||||||
@@ -80,12 +81,12 @@ export class NestFactoryStatic {
|
|||||||
* Creates an instance of the NestMicroservice (returns Promise)
|
* Creates an instance of the NestMicroservice (returns Promise)
|
||||||
*
|
*
|
||||||
* @param {} module Entry (root) application module class
|
* @param {} module Entry (root) application module class
|
||||||
* @param {NestMicroserviceOptions} options Optional microservice configuration
|
* @param {NestMicroserviceOptions & MicroserviceOptions} options Optional microservice configuration
|
||||||
* @returns an `Promise` of the INestMicroservice instance
|
* @returns an `Promise` of the INestMicroservice instance
|
||||||
*/
|
*/
|
||||||
public async createMicroservice(
|
public async createMicroservice(
|
||||||
module,
|
module,
|
||||||
options?: NestMicroserviceOptions,
|
options?: NestMicroserviceOptions & MicroserviceOptions,
|
||||||
): Promise<INestMicroservice> {
|
): Promise<INestMicroservice> {
|
||||||
if (!NestMicroservice) {
|
if (!NestMicroservice) {
|
||||||
throw new MicroservicesPackageNotFoundException();
|
throw new MicroservicesPackageNotFoundException();
|
||||||
|
|||||||
@@ -92,6 +92,10 @@ export class DependenciesScanner {
|
|||||||
token,
|
token,
|
||||||
metadata.COMPONENTS as 'components',
|
metadata.COMPONENTS as 'components',
|
||||||
),
|
),
|
||||||
|
...this.container.getDynamicMetadataByToken(
|
||||||
|
token,
|
||||||
|
metadata.PROVIDERS as 'providers',
|
||||||
|
),
|
||||||
];
|
];
|
||||||
components.map(component => {
|
components.map(component => {
|
||||||
this.storeComponent(component, token);
|
this.storeComponent(component, token);
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ describe('RoutesMapper', () => {
|
|||||||
middlewares: 'Test',
|
middlewares: 'Test',
|
||||||
forRoutes: [{ method: RequestMethod.GET }],
|
forRoutes: [{ method: RequestMethod.GET }],
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
mapper.mapRouteToRouteProps.bind(mapper, config.forRoutes[0]),
|
mapper.mapRouteToRouteProps.bind(mapper, config.forRoutes[0]),
|
||||||
).throws(UnknownRequestMappingException);
|
).throws(UnknownRequestMappingException);
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import { GUARDS_METADATA } from '../../common/constants';
|
|||||||
import { ApplicationConfig } from '../application-config';
|
import { ApplicationConfig } from '../application-config';
|
||||||
import { APP_INTERCEPTOR, APP_GUARD, APP_PIPE, APP_FILTER } from '../constants';
|
import { APP_INTERCEPTOR, APP_GUARD, APP_PIPE, APP_FILTER } from '../constants';
|
||||||
|
|
||||||
|
class Guard {}
|
||||||
|
|
||||||
describe('DependenciesScanner', () => {
|
describe('DependenciesScanner', () => {
|
||||||
@Component()
|
@Component()
|
||||||
class TestComponent {}
|
class TestComponent {}
|
||||||
@@ -123,7 +125,7 @@ describe('DependenciesScanner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
class CompMethod {
|
class CompMethod {
|
||||||
@UseGuards('test')
|
@UseGuards(Guard)
|
||||||
public method() {}
|
public method() {}
|
||||||
}
|
}
|
||||||
describe('reflectKeyMetadata', () => {
|
describe('reflectKeyMetadata', () => {
|
||||||
@@ -137,7 +139,7 @@ describe('DependenciesScanner', () => {
|
|||||||
GUARDS_METADATA,
|
GUARDS_METADATA,
|
||||||
'method',
|
'method',
|
||||||
);
|
);
|
||||||
expect(result).to.be.eql(['test']);
|
expect(result).to.be.eql([Guard]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as grpc from 'grpc';
|
import { GrpcObject } from 'grpc';
|
||||||
import { ClientProxy } from './client-proxy';
|
import { ClientProxy } from './client-proxy';
|
||||||
import { Logger } from '@nestjs/common/services/logger.service';
|
import { Logger } from '@nestjs/common/services/logger.service';
|
||||||
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
||||||
@@ -12,10 +12,6 @@ import { InvalidProtoDefinitionException } from '../exceptions/invalid-proto-def
|
|||||||
|
|
||||||
let grpcPackage: any = {};
|
let grpcPackage: any = {};
|
||||||
|
|
||||||
export interface GrpcService {
|
|
||||||
[name: string]: (...args: any[]) => Observable<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
|
export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
|
||||||
private readonly logger = new Logger(ClientProxy.name);
|
private readonly logger = new Logger(ClientProxy.name);
|
||||||
private readonly url: string;
|
private readonly url: string;
|
||||||
@@ -30,7 +26,7 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
|
|||||||
this.grpcClient = this.createClient();
|
this.grpcClient = this.createClient();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getService<T extends GrpcService = any>(name: string): T {
|
public getService<T = any>(name: string): T {
|
||||||
var options, credentials;
|
var options, credentials;
|
||||||
if (!this.grpcClient[name]) {
|
if (!this.grpcClient[name]) {
|
||||||
throw new InvalidGrpcServiceException();
|
throw new InvalidGrpcServiceException();
|
||||||
@@ -101,7 +97,7 @@ export class ClientGrpcProxy extends ClientProxy implements ClientGrpc {
|
|||||||
return grpcPackage;
|
return grpcPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public loadProto(): grpc.GrpcObject {
|
public loadProto(): GrpcObject {
|
||||||
try {
|
try {
|
||||||
const context = grpcPackage.load(
|
const context = grpcPackage.load(
|
||||||
this.getOptionsProp<GrpcOptions>(this.options, 'protoPath'),
|
this.getOptionsProp<GrpcOptions>(this.options, 'protoPath'),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as mqtt from 'mqtt';
|
import { MqttClient } from 'mqtt';
|
||||||
import { ClientProxy } from './client-proxy';
|
import { ClientProxy } from './client-proxy';
|
||||||
import { Logger } from '@nestjs/common/services/logger.service';
|
import { Logger } from '@nestjs/common/services/logger.service';
|
||||||
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
||||||
@@ -17,7 +17,7 @@ let mqttPackage: any = {};
|
|||||||
export class ClientMqtt extends ClientProxy {
|
export class ClientMqtt extends ClientProxy {
|
||||||
private readonly logger = new Logger(ClientProxy.name);
|
private readonly logger = new Logger(ClientProxy.name);
|
||||||
private readonly url: string;
|
private readonly url: string;
|
||||||
private mqttClient: mqtt.MqttClient;
|
private mqttClient: MqttClient;
|
||||||
|
|
||||||
constructor(private readonly options: ClientOptions) {
|
constructor(private readonly options: ClientOptions) {
|
||||||
super();
|
super();
|
||||||
@@ -86,11 +86,11 @@ export class ClientMqtt extends ClientProxy {
|
|||||||
this.handleError(this.mqttClient, callback);
|
this.handleError(this.mqttClient, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createClient(): mqtt.MqttClient {
|
public createClient(): MqttClient {
|
||||||
return mqttPackage.connect(this.url, this.options.options as MqttOptions);
|
return mqttPackage.connect(this.url, this.options.options as MqttOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleError(client: mqtt.MqttClient, callback: (...args) => any) {
|
public handleError(client: MqttClient, callback: (...args) => any) {
|
||||||
const errorCallback = err => {
|
const errorCallback = err => {
|
||||||
if (err.code === 'ECONNREFUSED') {
|
if (err.code === 'ECONNREFUSED') {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as nats from 'nats';
|
import { Client } from 'nats';
|
||||||
import { ClientProxy } from './client-proxy';
|
import { ClientProxy } from './client-proxy';
|
||||||
import { Logger } from '@nestjs/common/services/logger.service';
|
import { Logger } from '@nestjs/common/services/logger.service';
|
||||||
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
||||||
@@ -15,7 +15,7 @@ let natsPackage: any = {};
|
|||||||
export class ClientNats extends ClientProxy {
|
export class ClientNats extends ClientProxy {
|
||||||
private readonly logger = new Logger(ClientProxy.name);
|
private readonly logger = new Logger(ClientProxy.name);
|
||||||
private readonly url: string;
|
private readonly url: string;
|
||||||
private natsClient: nats.Client;
|
private natsClient: Client;
|
||||||
|
|
||||||
constructor(private readonly options: ClientOptions) {
|
constructor(private readonly options: ClientOptions) {
|
||||||
super();
|
super();
|
||||||
@@ -80,7 +80,7 @@ export class ClientNats extends ClientProxy {
|
|||||||
this.handleError(this.natsClient, callback);
|
this.handleError(this.natsClient, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createClient(): Promise<nats.Client> {
|
public createClient(): Promise<Client> {
|
||||||
const options = this.options.options || ({} as NatsOptions);
|
const options = this.options.options || ({} as NatsOptions);
|
||||||
const client = natsPackage.connect({
|
const client = natsPackage.connect({
|
||||||
...(options as any),
|
...(options as any),
|
||||||
@@ -90,7 +90,7 @@ export class ClientNats extends ClientProxy {
|
|||||||
return new Promise(resolve => client.on(CONNECT_EVENT, resolve));
|
return new Promise(resolve => client.on(CONNECT_EVENT, resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleError(client: nats.Client, callback: (...args) => any) {
|
public handleError(client: Client, callback: (...args) => any) {
|
||||||
const errorCallback = err => {
|
const errorCallback = err => {
|
||||||
if (err.code === 'ECONNREFUSED') {
|
if (err.code === 'ECONNREFUSED') {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { ReadPacket, PacketId, WritePacket, ClientOptions } from './../interface
|
|||||||
import { MissingRequiredDependencyException } from '@nestjs/core/errors/exceptions/missing-dependency.exception';
|
import { MissingRequiredDependencyException } from '@nestjs/core/errors/exceptions/missing-dependency.exception';
|
||||||
|
|
||||||
export abstract class ClientProxy {
|
export abstract class ClientProxy {
|
||||||
|
public abstract close(): any;
|
||||||
protected abstract publish(
|
protected abstract publish(
|
||||||
packet: ReadPacket,
|
packet: ReadPacket,
|
||||||
callback: (packet: WritePacket) => void,
|
callback: (packet: WritePacket) => void,
|
||||||
@@ -50,9 +51,9 @@ export abstract class ClientProxy {
|
|||||||
return Object.assign(packet, { id });
|
return Object.assign(packet, { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getOptionsProp<T>(
|
protected getOptionsProp<T extends { options? }>(
|
||||||
obj: ClientOptions,
|
obj: ClientOptions,
|
||||||
prop: keyof T,
|
prop: keyof T['options'],
|
||||||
defaultValue = undefined,
|
defaultValue = undefined,
|
||||||
) {
|
) {
|
||||||
return obj && obj.options ? obj.options[prop as any] : defaultValue;
|
return obj && obj.options ? obj.options[prop as any] : defaultValue;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as redis from 'redis';
|
import { ClientOpts, RetryStrategyOptions, RedisClient } from 'redis';
|
||||||
import { ClientProxy } from './client-proxy';
|
import { ClientProxy } from './client-proxy';
|
||||||
import { Logger } from '@nestjs/common/services/logger.service';
|
import { Logger } from '@nestjs/common/services/logger.service';
|
||||||
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
import { ClientOptions } from '../interfaces/client-metadata.interface';
|
||||||
@@ -21,8 +21,8 @@ let redisPackage: any = {};
|
|||||||
export class ClientRedis extends ClientProxy {
|
export class ClientRedis extends ClientProxy {
|
||||||
private readonly logger = new Logger(ClientProxy.name);
|
private readonly logger = new Logger(ClientProxy.name);
|
||||||
private readonly url: string;
|
private readonly url: string;
|
||||||
private pubClient: redis.RedisClient;
|
private pubClient: RedisClient;
|
||||||
private subClient: redis.RedisClient;
|
private subClient: RedisClient;
|
||||||
private isExplicitlyTerminated = false;
|
private isExplicitlyTerminated = false;
|
||||||
|
|
||||||
constructor(private readonly options: ClientOptions) {
|
constructor(private readonly options: ClientOptions) {
|
||||||
@@ -107,14 +107,14 @@ export class ClientRedis extends ClientProxy {
|
|||||||
this.handleError(this.subClient, callback);
|
this.handleError(this.subClient, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public createClient(): redis.RedisClient {
|
public createClient(): RedisClient {
|
||||||
return redisPackage.createClient({
|
return redisPackage.createClient({
|
||||||
...this.getClientOptions(),
|
...this.getClientOptions(),
|
||||||
url: this.url,
|
url: this.url,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public handleError(client: redis.RedisClient, callback: (...args) => any) {
|
public handleError(client: RedisClient, callback: (...args) => any) {
|
||||||
const errorCallback = err => {
|
const errorCallback = err => {
|
||||||
if (err.code === 'ECONNREFUSED') {
|
if (err.code === 'ECONNREFUSED') {
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
@@ -129,7 +129,7 @@ export class ClientRedis extends ClientProxy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getClientOptions(): Partial<redis.ClientOpts> {
|
public getClientOptions(): Partial<ClientOpts> {
|
||||||
const retry_strategy = options => this.createRetryStrategy(options);
|
const retry_strategy = options => this.createRetryStrategy(options);
|
||||||
return {
|
return {
|
||||||
retry_strategy,
|
retry_strategy,
|
||||||
@@ -137,7 +137,7 @@ export class ClientRedis extends ClientProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createRetryStrategy(
|
public createRetryStrategy(
|
||||||
options: redis.RetryStrategyOptions,
|
options: RetryStrategyOptions,
|
||||||
): undefined | number {
|
): undefined | number {
|
||||||
if (
|
if (
|
||||||
this.isExplicitlyTerminated ||
|
this.isExplicitlyTerminated ||
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
export * from './client-proxy';
|
export * from './client-proxy';
|
||||||
export * from './client-proxy-factory';
|
export * from './client-proxy-factory';
|
||||||
export { GrpcService } from './client-grpc';
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import { GrpcService } from '../client/client-grpc';
|
|
||||||
|
|
||||||
export interface ClientGrpc {
|
export interface ClientGrpc {
|
||||||
getService<T extends GrpcService = any>(name: string): T;
|
getService<T = any>(name: string): T;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ import {
|
|||||||
GrpcOptions,
|
GrpcOptions,
|
||||||
} from './microservice-configuration.interface';
|
} from './microservice-configuration.interface';
|
||||||
|
|
||||||
export interface ClientOptions {
|
export type ClientOptions =
|
||||||
transport?: Transport;
|
| RedisOptions
|
||||||
options?:
|
| NatsOptions
|
||||||
| TcpClientOptions
|
| MqttOptions
|
||||||
| RedisOptions
|
| GrpcOptions
|
||||||
| NatsOptions
|
| TcpClientOptions;
|
||||||
| MqttOptions
|
|
||||||
| GrpcOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TcpClientOptions {
|
export interface TcpClientOptions {
|
||||||
host?: string;
|
transport: Transport.TCP;
|
||||||
port?: number;
|
options?: {
|
||||||
|
host?: string;
|
||||||
|
port?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,46 +4,64 @@ import { IClientOptions } from 'mqtt';
|
|||||||
import { ServerCredentials } from 'grpc';
|
import { ServerCredentials } from 'grpc';
|
||||||
import { Server } from './../server/server';
|
import { Server } from './../server/server';
|
||||||
|
|
||||||
export interface MicroserviceOptions {
|
export type MicroserviceOptions =
|
||||||
transport?: Transport;
|
| GrpcOptions
|
||||||
strategy?: Server & CustomTransportStrategy;
|
| TcpOptions
|
||||||
options?:
|
| RedisOptions
|
||||||
| TcpOptions
|
| NatsOptions
|
||||||
| RedisOptions
|
| MqttOptions
|
||||||
| NatsOptions
|
| CustomStrategy;
|
||||||
| MqttOptions;
|
|
||||||
|
export interface CustomStrategy {
|
||||||
|
strategy: Server & CustomTransportStrategy;
|
||||||
|
options?: {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GrpcOptions {
|
export interface GrpcOptions {
|
||||||
url?: string;
|
transport?: Transport.GRPC;
|
||||||
credentials?: ServerCredentials;
|
options: {
|
||||||
protoPath: string;
|
url?: string;
|
||||||
package: string;
|
credentials?: ServerCredentials;
|
||||||
|
protoPath: string;
|
||||||
|
package: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TcpOptions {
|
export interface TcpOptions {
|
||||||
host?: string;
|
transport?: Transport.TCP;
|
||||||
port?: number;
|
options?: {
|
||||||
retryAttempts?: number;
|
host?: string;
|
||||||
retryDelay?: number;
|
port?: number;
|
||||||
|
retryAttempts?: number;
|
||||||
|
retryDelay?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RedisOptions {
|
export interface RedisOptions {
|
||||||
url?: string;
|
transport?: Transport.REDIS;
|
||||||
retryAttempts?: number;
|
options?: {
|
||||||
retryDelay?: number;
|
url?: string;
|
||||||
|
retryAttempts?: number;
|
||||||
|
retryDelay?: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MqttOptions extends IClientOptions {
|
export interface MqttOptions {
|
||||||
url?: string;
|
transport?: Transport.MQTT;
|
||||||
|
options?: IClientOptions & {
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NatsOptions {
|
export interface NatsOptions {
|
||||||
url?: string;
|
transport?: Transport.NATS;
|
||||||
name?: string;
|
options?: {
|
||||||
pass?: string;
|
url?: string;
|
||||||
maxReconnectAttempts?: number;
|
name?: string;
|
||||||
reconnectTimeWait?: number;
|
pass?: string;
|
||||||
servers?: string[];
|
maxReconnectAttempts?: number;
|
||||||
tls?: any;
|
reconnectTimeWait?: number;
|
||||||
}
|
servers?: string[];
|
||||||
|
tls?: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,14 +59,20 @@ export class NestMicroservice extends NestApplicationContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createServer(config: MicroserviceOptions) {
|
public createServer(config: MicroserviceOptions) {
|
||||||
this.microserviceConfig = {
|
try {
|
||||||
transport: Transport.TCP,
|
this.microserviceConfig = {
|
||||||
...config,
|
transport: Transport.TCP,
|
||||||
};
|
...config,
|
||||||
const { strategy } = config;
|
} as any;
|
||||||
this.server = strategy
|
const { strategy } = config as any;
|
||||||
? strategy
|
this.server = strategy
|
||||||
: ServerFactory.create(this.microserviceConfig);
|
? strategy
|
||||||
|
: ServerFactory.create(this.microserviceConfig);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
this.logger.error(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public registerModules() {
|
public registerModules() {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class ServerFactory {
|
|||||||
public static create(
|
public static create(
|
||||||
options: MicroserviceOptions,
|
options: MicroserviceOptions,
|
||||||
): Server & CustomTransportStrategy {
|
): Server & CustomTransportStrategy {
|
||||||
const { transport } = options;
|
const { transport } = options as any;
|
||||||
switch (transport) {
|
switch (transport) {
|
||||||
case Transport.REDIS:
|
case Transport.REDIS:
|
||||||
return new ServerRedis(options);
|
return new ServerRedis(options);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as grpc from 'grpc';
|
import { GrpcObject } from 'grpc';
|
||||||
import { Server } from './server';
|
import { Server } from './server';
|
||||||
import {
|
import {
|
||||||
MicroserviceOptions,
|
MicroserviceOptions,
|
||||||
@@ -8,6 +8,7 @@ import { CustomTransportStrategy } from './../interfaces';
|
|||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { GRPC_DEFAULT_URL } from './../constants';
|
import { GRPC_DEFAULT_URL } from './../constants';
|
||||||
import { InvalidGrpcPackageException } from '../exceptions/invalid-grpc-package.exception';
|
import { InvalidGrpcPackageException } from '../exceptions/invalid-grpc-package.exception';
|
||||||
|
import { InvalidProtoDefinitionException } from '../exceptions/invalid-proto-definition.exception';
|
||||||
|
|
||||||
let grpcPackage: any = {};
|
let grpcPackage: any = {};
|
||||||
|
|
||||||
@@ -35,9 +36,7 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async bindEvents() {
|
public async bindEvents() {
|
||||||
const grpcContext = grpcPackage.load(
|
const grpcContext = this.loadProto();
|
||||||
this.getOptionsProp<GrpcOptions>(this.options, 'protoPath'),
|
|
||||||
);
|
|
||||||
const packageName = this.getOptionsProp<GrpcOptions>(
|
const packageName = this.getOptionsProp<GrpcOptions>(
|
||||||
this.options,
|
this.options,
|
||||||
'package',
|
'package',
|
||||||
@@ -146,4 +145,16 @@ export class ServerGrpc extends Server implements CustomTransportStrategy {
|
|||||||
}
|
}
|
||||||
return pkg;
|
return pkg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public loadProto(): GrpcObject {
|
||||||
|
try {
|
||||||
|
const context = grpcPackage.load(
|
||||||
|
this.getOptionsProp<GrpcOptions>(this.options, 'protoPath'),
|
||||||
|
);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw new InvalidProtoDefinitionException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as mqtt from 'mqtt';
|
import { MqttClient } from 'mqtt';
|
||||||
import { Server } from './server';
|
import { Server } from './server';
|
||||||
import { NO_PATTERN_MESSAGE } from '../constants';
|
import { NO_PATTERN_MESSAGE } from '../constants';
|
||||||
import {
|
import {
|
||||||
@@ -22,7 +22,7 @@ let mqttPackage: any = {};
|
|||||||
|
|
||||||
export class ServerMqtt extends Server implements CustomTransportStrategy {
|
export class ServerMqtt extends Server implements CustomTransportStrategy {
|
||||||
private readonly url: string;
|
private readonly url: string;
|
||||||
private mqttClient: mqtt.MqttClient;
|
private mqttClient:MqttClient;
|
||||||
|
|
||||||
constructor(private readonly options: MicroserviceOptions) {
|
constructor(private readonly options: MicroserviceOptions) {
|
||||||
super();
|
super();
|
||||||
@@ -44,7 +44,7 @@ export class ServerMqtt extends Server implements CustomTransportStrategy {
|
|||||||
this.mqttClient.on(CONNECT_EVENT, callback);
|
this.mqttClient.on(CONNECT_EVENT, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bindEvents(mqttClient: mqtt.MqttClient) {
|
public bindEvents(mqttClient: MqttClient) {
|
||||||
mqttClient.on(MESSAGE_EVENT, this.getMessageHandler(mqttClient).bind(this));
|
mqttClient.on(MESSAGE_EVENT, this.getMessageHandler(mqttClient).bind(this));
|
||||||
const registeredPatterns = Object.keys(this.messageHandlers);
|
const registeredPatterns = Object.keys(this.messageHandlers);
|
||||||
registeredPatterns.forEach(pattern =>
|
registeredPatterns.forEach(pattern =>
|
||||||
@@ -56,11 +56,11 @@ export class ServerMqtt extends Server implements CustomTransportStrategy {
|
|||||||
this.mqttClient && this.mqttClient.end();
|
this.mqttClient && this.mqttClient.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
public createMqttClient(): mqtt.MqttClient {
|
public createMqttClient(): MqttClient {
|
||||||
return mqttPackage.connect(this.url, this.options.options as MqttOptions);
|
return mqttPackage.connect(this.url, this.options.options as MqttOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMessageHandler(pub: mqtt.MqttClient): any {
|
public getMessageHandler(pub: MqttClient): any {
|
||||||
return async (channel, buffer) =>
|
return async (channel, buffer) =>
|
||||||
await this.handleMessage(channel, buffer, pub);
|
await this.handleMessage(channel, buffer, pub);
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ export class ServerMqtt extends Server implements CustomTransportStrategy {
|
|||||||
public async handleMessage(
|
public async handleMessage(
|
||||||
channel: string,
|
channel: string,
|
||||||
buffer: Buffer,
|
buffer: Buffer,
|
||||||
pub: mqtt.MqttClient,
|
pub: MqttClient,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const packet = this.deserialize(buffer.toString());
|
const packet = this.deserialize(buffer.toString());
|
||||||
const pattern = channel.replace(/_ack$/, '');
|
const pattern = channel.replace(/_ack$/, '');
|
||||||
@@ -85,7 +85,7 @@ export class ServerMqtt extends Server implements CustomTransportStrategy {
|
|||||||
response$ && this.send(response$, publish);
|
response$ && this.send(response$, publish);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPublisher(client: mqtt.MqttClient, pattern: any, id: string): any {
|
public getPublisher(client: MqttClient, pattern: any, id: string): any {
|
||||||
return response =>
|
return response =>
|
||||||
client.publish(
|
client.publish(
|
||||||
this.getResQueueName(pattern),
|
this.getResQueueName(pattern),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as nats from 'nats';
|
import { Client } from 'nats';
|
||||||
import { Server } from './server';
|
import { Server } from './server';
|
||||||
import { NO_PATTERN_MESSAGE } from '../constants';
|
import { NO_PATTERN_MESSAGE } from '../constants';
|
||||||
import {
|
import {
|
||||||
@@ -17,7 +17,7 @@ let natsPackage: any = {};
|
|||||||
|
|
||||||
export class ServerNats extends Server implements CustomTransportStrategy {
|
export class ServerNats extends Server implements CustomTransportStrategy {
|
||||||
private readonly url: string;
|
private readonly url: string;
|
||||||
private natsClient: nats.Client;
|
private natsClient: Client;
|
||||||
|
|
||||||
constructor(private readonly options: MicroserviceOptions) {
|
constructor(private readonly options: MicroserviceOptions) {
|
||||||
super();
|
super();
|
||||||
@@ -38,7 +38,7 @@ export class ServerNats extends Server implements CustomTransportStrategy {
|
|||||||
this.natsClient.on(CONNECT_EVENT, callback);
|
this.natsClient.on(CONNECT_EVENT, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bindEvents(client: nats.Client) {
|
public bindEvents(client: Client) {
|
||||||
const registeredPatterns = Object.keys(this.messageHandlers);
|
const registeredPatterns = Object.keys(this.messageHandlers);
|
||||||
registeredPatterns.forEach(pattern => {
|
registeredPatterns.forEach(pattern => {
|
||||||
const channel = this.getAckQueueName(pattern);
|
const channel = this.getAckQueueName(pattern);
|
||||||
@@ -54,7 +54,7 @@ export class ServerNats extends Server implements CustomTransportStrategy {
|
|||||||
this.natsClient = null;
|
this.natsClient = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createNatsClient(): nats.Client {
|
public createNatsClient(): Client {
|
||||||
const options = this.options.options || ({} as NatsOptions);
|
const options = this.options.options || ({} as NatsOptions);
|
||||||
return natsPackage.connect({
|
return natsPackage.connect({
|
||||||
...(options as any),
|
...(options as any),
|
||||||
@@ -63,14 +63,14 @@ export class ServerNats extends Server implements CustomTransportStrategy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMessageHandler(channel: string, client: nats.Client) {
|
public getMessageHandler(channel: string, client: Client) {
|
||||||
return async buffer => await this.handleMessage(channel, buffer, client);
|
return async buffer => await this.handleMessage(channel, buffer, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handleMessage(
|
public async handleMessage(
|
||||||
channel: string,
|
channel: string,
|
||||||
message: ReadPacket & PacketId,
|
message: ReadPacket & PacketId,
|
||||||
client: nats.Client,
|
client: Client,
|
||||||
) {
|
) {
|
||||||
const pattern = channel.replace(/_ack$/, '');
|
const pattern = channel.replace(/_ack$/, '');
|
||||||
const publish = this.getPublisher(client, pattern, message.id);
|
const publish = this.getPublisher(client, pattern, message.id);
|
||||||
@@ -86,7 +86,7 @@ export class ServerNats extends Server implements CustomTransportStrategy {
|
|||||||
response$ && this.send(response$, publish);
|
response$ && this.send(response$, publish);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPublisher(publisher: nats.Client, pattern: any, id: string) {
|
public getPublisher(publisher: Client, pattern: any, id: string) {
|
||||||
return response =>
|
return response =>
|
||||||
publisher.publish(this.getResQueueName(pattern), Object.assign(response, {
|
publisher.publish(this.getResQueueName(pattern), Object.assign(response, {
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as redis from 'redis';
|
import { RedisClient, ClientOpts, RetryStrategyOptions } from 'redis';
|
||||||
import { Server } from './server';
|
import { Server } from './server';
|
||||||
import { NO_PATTERN_MESSAGE } from '../constants';
|
import { NO_PATTERN_MESSAGE } from '../constants';
|
||||||
import {
|
import {
|
||||||
@@ -22,8 +22,8 @@ let redisPackage: any = {};
|
|||||||
|
|
||||||
export class ServerRedis extends Server implements CustomTransportStrategy {
|
export class ServerRedis extends Server implements CustomTransportStrategy {
|
||||||
private readonly url: string;
|
private readonly url: string;
|
||||||
private subClient: redis.RedisClient;
|
private subClient: RedisClient;
|
||||||
private pubClient: redis.RedisClient;
|
private pubClient: RedisClient;
|
||||||
private isExplicitlyTerminated = false;
|
private isExplicitlyTerminated = false;
|
||||||
|
|
||||||
constructor(private readonly options: MicroserviceOptions) {
|
constructor(private readonly options: MicroserviceOptions) {
|
||||||
@@ -50,12 +50,12 @@ export class ServerRedis extends Server implements CustomTransportStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bindEvents(
|
public bindEvents(
|
||||||
subClient: redis.RedisClient,
|
subClient: RedisClient,
|
||||||
pubClient: redis.RedisClient,
|
pubClient: RedisClient,
|
||||||
) {
|
) {
|
||||||
subClient.on(MESSAGE_EVENT, this.getMessageHandler(pubClient).bind(this));
|
subClient.on(MESSAGE_EVENT, this.getMessageHandler(pubClient).bind(this));
|
||||||
const registeredPatterns = Object.keys(this.messageHandlers);
|
const subscribePatterns = Object.keys(this.messageHandlers);
|
||||||
registeredPatterns.forEach(pattern =>
|
subscribePatterns.forEach(pattern =>
|
||||||
subClient.subscribe(this.getAckQueueName(pattern)),
|
subClient.subscribe(this.getAckQueueName(pattern)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -66,14 +66,14 @@ export class ServerRedis extends Server implements CustomTransportStrategy {
|
|||||||
this.subClient && this.subClient.quit();
|
this.subClient && this.subClient.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public createRedisClient(): redis.RedisClient {
|
public createRedisClient(): RedisClient {
|
||||||
return redisPackage.createClient({
|
return redisPackage.createClient({
|
||||||
...this.getClientOptions(),
|
...this.getClientOptions(),
|
||||||
url: this.url,
|
url: this.url,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMessageHandler(pub: redis.RedisClient) {
|
public getMessageHandler(pub: RedisClient) {
|
||||||
return async (channel, buffer) =>
|
return async (channel, buffer) =>
|
||||||
await this.handleMessage(channel, buffer, pub);
|
await this.handleMessage(channel, buffer, pub);
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ export class ServerRedis extends Server implements CustomTransportStrategy {
|
|||||||
public async handleMessage(
|
public async handleMessage(
|
||||||
channel,
|
channel,
|
||||||
buffer: string | any,
|
buffer: string | any,
|
||||||
pub: redis.RedisClient,
|
pub: RedisClient,
|
||||||
) {
|
) {
|
||||||
const packet = this.deserialize(buffer);
|
const packet = this.deserialize(buffer);
|
||||||
const pattern = channel.replace(/_ack$/, '');
|
const pattern = channel.replace(/_ack$/, '');
|
||||||
@@ -98,7 +98,7 @@ export class ServerRedis extends Server implements CustomTransportStrategy {
|
|||||||
response$ && this.send(response$, publish);
|
response$ && this.send(response$, publish);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPublisher(pub: redis.RedisClient, pattern: any, id: string) {
|
public getPublisher(pub: RedisClient, pattern: any, id: string) {
|
||||||
return response =>
|
return response =>
|
||||||
pub.publish(
|
pub.publish(
|
||||||
this.getResQueueName(pattern),
|
this.getResQueueName(pattern),
|
||||||
@@ -126,7 +126,7 @@ export class ServerRedis extends Server implements CustomTransportStrategy {
|
|||||||
stream.on(ERROR_EVENT, err => this.logger.error(err));
|
stream.on(ERROR_EVENT, err => this.logger.error(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
public getClientOptions(): Partial<redis.ClientOpts> {
|
public getClientOptions(): Partial<ClientOpts> {
|
||||||
const retry_strategy = options => this.createRetryStrategy(options);
|
const retry_strategy = options => this.createRetryStrategy(options);
|
||||||
return {
|
return {
|
||||||
retry_strategy,
|
retry_strategy,
|
||||||
@@ -134,7 +134,7 @@ export class ServerRedis extends Server implements CustomTransportStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public createRetryStrategy(
|
public createRetryStrategy(
|
||||||
options: redis.RetryStrategyOptions,
|
options: RetryStrategyOptions,
|
||||||
): undefined | number {
|
): undefined | number {
|
||||||
if (
|
if (
|
||||||
this.isExplicitlyTerminated ||
|
this.isExplicitlyTerminated ||
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export abstract class Server {
|
|||||||
.subscribe(response => respond({ err: null, response }));
|
.subscribe(response => respond({ err: null, response }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public transformToObservable(resultOrDeffered) {
|
public transformToObservable<T = any>(resultOrDeffered): Observable<T> {
|
||||||
if (resultOrDeffered instanceof Promise) {
|
if (resultOrDeffered instanceof Promise) {
|
||||||
return fromPromise(resultOrDeffered);
|
return fromPromise(resultOrDeffered);
|
||||||
} else if (!(resultOrDeffered && isFunction(resultOrDeffered.subscribe))) {
|
} else if (!(resultOrDeffered && isFunction(resultOrDeffered.subscribe))) {
|
||||||
@@ -53,9 +53,9 @@ export abstract class Server {
|
|||||||
return resultOrDeffered;
|
return resultOrDeffered;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getOptionsProp<T>(
|
public getOptionsProp<T extends { options? }>(
|
||||||
obj: MicroserviceOptions,
|
obj: MicroserviceOptions,
|
||||||
prop: keyof T,
|
prop: keyof T['options'],
|
||||||
defaultValue = undefined,
|
defaultValue = undefined,
|
||||||
) {
|
) {
|
||||||
return obj && obj.options ? obj.options[prop as any] : defaultValue;
|
return obj && obj.options ? obj.options[prop as any] : defaultValue;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
|
|||||||
|
|
||||||
class TestClientProxy extends ClientProxy {
|
class TestClientProxy extends ClientProxy {
|
||||||
public publish(pattern, callback) {}
|
public publish(pattern, callback) {}
|
||||||
|
public close() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('ClientProxy', () => {
|
describe('ClientProxy', () => {
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ describe('ListenerMetadataExplorer', () => {
|
|||||||
const clientSecMetadata = { transport: Transport.REDIS };
|
const clientSecMetadata = { transport: Transport.REDIS };
|
||||||
|
|
||||||
class Test {
|
class Test {
|
||||||
@Client(clientMetadata) public client;
|
@Client(clientMetadata as any) public client;
|
||||||
@Client(clientSecMetadata) public redisClient;
|
@Client(clientSecMetadata as any) public redisClient;
|
||||||
|
|
||||||
get testGet() {
|
get testGet() {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -43,8 +43,10 @@ describe('ServerFactory', () => {
|
|||||||
|
|
||||||
it(`should return grpc server`, () => {
|
it(`should return grpc server`, () => {
|
||||||
expect(
|
expect(
|
||||||
ServerFactory.create({ transport: Transport.GRPC }) instanceof
|
ServerFactory.create({
|
||||||
ServerGrpc,
|
transport: Transport.GRPC,
|
||||||
|
options: { protoPath: '', package: '' },
|
||||||
|
}) instanceof ServerGrpc,
|
||||||
).to.be.true;
|
).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
InstanceWrapper,
|
InstanceWrapper,
|
||||||
} from '@nestjs/core/injector/container';
|
} from '@nestjs/core/injector/container';
|
||||||
import { ModuleMetadata } from '@nestjs/common/interfaces/modules/module-metadata.interface';
|
import { ModuleMetadata } from '@nestjs/common/interfaces/modules/module-metadata.interface';
|
||||||
import { Module } from '@nestjs/common/utils/decorators/module.decorator';
|
|
||||||
import { DependenciesScanner } from '@nestjs/core/scanner';
|
import { DependenciesScanner } from '@nestjs/core/scanner';
|
||||||
import { InstanceLoader } from '@nestjs/core/injector/instance-loader';
|
import { InstanceLoader } from '@nestjs/core/injector/instance-loader';
|
||||||
import { Logger } from '@nestjs/common/services/logger.service';
|
import { Logger } from '@nestjs/common/services/logger.service';
|
||||||
|
|||||||
1
packages/websockets/adapters/index.ts
Normal file
1
packages/websockets/adapters/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './ws-adapter';
|
||||||
@@ -1,16 +1,30 @@
|
|||||||
import * as WebSocket from 'ws';
|
|
||||||
import { Server } from 'http';
|
import { Server } from 'http';
|
||||||
import { MessageMappingProperties } from '../gateway-metadata-explorer';
|
import { MessageMappingProperties } from '../gateway-metadata-explorer';
|
||||||
import { CONNECTION_EVENT, DISCONNECT_EVENT, CLOSE_EVENT } from '../constants';
|
import {
|
||||||
import { WebSocketAdapter } from '@nestjs/common';
|
CONNECTION_EVENT,
|
||||||
|
DISCONNECT_EVENT,
|
||||||
|
CLOSE_EVENT,
|
||||||
|
ERROR_EVENT,
|
||||||
|
} from '../constants';
|
||||||
|
import { WebSocketAdapter, Logger } from '@nestjs/common';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { mergeMap, filter, tap } from 'rxjs/operators';
|
import { mergeMap, filter, tap } from 'rxjs/operators';
|
||||||
import { fromEvent } from 'rxjs/observable/fromEvent';
|
import { fromEvent } from 'rxjs/observable/fromEvent';
|
||||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||||
import 'rxjs/add/observable/empty';
|
import { empty } from 'rxjs/observable/empty';
|
||||||
|
import { MissingRequiredDependencyException } from '@nestjs/core/errors/exceptions/missing-dependency.exception';
|
||||||
|
|
||||||
|
let wsPackage: any = {};
|
||||||
|
|
||||||
export class WsAdapter implements WebSocketAdapter {
|
export class WsAdapter implements WebSocketAdapter {
|
||||||
constructor(private readonly httpServer: Server | null = null) {}
|
private readonly logger = new Logger(WsAdapter.name);
|
||||||
|
constructor(private readonly httpServer: Server | null = null) {
|
||||||
|
try {
|
||||||
|
wsPackage = require('ws');
|
||||||
|
} catch (e) {
|
||||||
|
throw new MissingRequiredDependencyException('ws', 'WsAdapter');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public create(
|
public create(
|
||||||
port: number,
|
port: number,
|
||||||
@@ -18,17 +32,21 @@ export class WsAdapter implements WebSocketAdapter {
|
|||||||
): any {
|
): any {
|
||||||
const { server, ...wsOptions } = options;
|
const { server, ...wsOptions } = options;
|
||||||
if (port === 0 && this.httpServer) {
|
if (port === 0 && this.httpServer) {
|
||||||
return new WebSocket.Server({
|
return this.bindErrorHandler(
|
||||||
server: this.httpServer,
|
new wsPackage.Server({
|
||||||
...wsOptions,
|
server: this.httpServer,
|
||||||
});
|
...wsOptions,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return server
|
return server
|
||||||
? server
|
? server
|
||||||
: new WebSocket.Server({
|
: this.bindErrorHandler(
|
||||||
port,
|
new wsPackage.Server({
|
||||||
...wsOptions,
|
port,
|
||||||
});
|
...wsOptions,
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bindClientConnect(server, callback: (...args) => void) {
|
public bindClientConnect(server, callback: (...args) => void) {
|
||||||
@@ -62,7 +80,7 @@ export class WsAdapter implements WebSocketAdapter {
|
|||||||
handler => handler.message === message.event,
|
handler => handler.message === message.event,
|
||||||
);
|
);
|
||||||
if (!messageHandler) {
|
if (!messageHandler) {
|
||||||
return Observable.empty();
|
return empty();
|
||||||
}
|
}
|
||||||
const { callback } = messageHandler;
|
const { callback } = messageHandler;
|
||||||
return process(callback(message.data));
|
return process(callback(message.data));
|
||||||
@@ -71,4 +89,9 @@ export class WsAdapter implements WebSocketAdapter {
|
|||||||
public close(server) {
|
public close(server) {
|
||||||
isFunction(server.close) && server.close();
|
isFunction(server.close) && server.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bindErrorHandler(server) {
|
||||||
|
server.on(ERROR_EVENT, err => this.logger.error(err));
|
||||||
|
return server;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ export const GATEWAY_OPTIONS = '__gatewayOptions';
|
|||||||
|
|
||||||
export const CONNECTION_EVENT = 'connection';
|
export const CONNECTION_EVENT = 'connection';
|
||||||
export const DISCONNECT_EVENT = 'disconnect';
|
export const DISCONNECT_EVENT = 'disconnect';
|
||||||
export const CLOSE_EVENT = 'close';
|
export const CLOSE_EVENT = 'close';
|
||||||
|
export const ERROR_EVENT = 'error';
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
"@nestjs/microservices": "^5.0.0",
|
"@nestjs/microservices": "^5.0.0",
|
||||||
"@nestjs/testing": "^5.0.0",
|
"@nestjs/testing": "^5.0.0",
|
||||||
"@nestjs/websockets": "^5.0.0",
|
"@nestjs/websockets": "^5.0.0",
|
||||||
"amqplib": "^0.5.2",
|
|
||||||
"class-transformer": "^0.1.7",
|
"class-transformer": "^0.1.7",
|
||||||
"class-validator": "^0.8.1",
|
"class-validator": "^0.8.1",
|
||||||
"fastify": "^1.1.0",
|
"fastify": "^1.1.0",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { CatsModule } from './cats/cats.module';
|
import { CatsModule } from './cats/cats.module';
|
||||||
import { CatsController } from './cats/cats.controller';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [CatsModule],
|
imports: [CatsModule],
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Cat } from './interfaces/cat.interface';
|
import { Cat } from './interfaces/cat.interface';
|
||||||
import { CatsModule } from './cats.module';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CatsService {
|
export class CatsService {
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { HttpException } from '@nestjs/common';
|
|
||||||
import { HttpStatus } from '@nestjs/common';
|
|
||||||
|
|
||||||
export class ForbiddenException extends HttpException {
|
|
||||||
constructor() {
|
|
||||||
super('Forbidden', HttpStatus.FORBIDDEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
import { ExceptionFilter, Catch } from '@nestjs/common';
|
import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';
|
||||||
import { HttpException } from '@nestjs/common';
|
import { HttpException } from '@nestjs/common';
|
||||||
|
|
||||||
@Catch(HttpException)
|
@Catch(HttpException)
|
||||||
export class HttpExceptionFilter implements ExceptionFilter {
|
export class HttpExceptionFilter implements ExceptionFilter {
|
||||||
catch(exception: HttpException, response) {
|
catch(exception: HttpException, host: ArgumentsHost) {
|
||||||
const status = exception.getStatus();
|
const status = exception.getStatus();
|
||||||
|
const response = host.switchToHttp().getResponse();
|
||||||
|
|
||||||
response
|
response.status(status).json({
|
||||||
.status(status)
|
statusCode: status,
|
||||||
.json({
|
message: `This is a message from the exception filter`,
|
||||||
statusCode: status,
|
});
|
||||||
message: `This is a message from the exception filter`,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import { Reflector } from '@nestjs/core';
|
|||||||
export class RolesGuard implements CanActivate {
|
export class RolesGuard implements CanActivate {
|
||||||
constructor(private readonly reflector: Reflector) {}
|
constructor(private readonly reflector: Reflector) {}
|
||||||
|
|
||||||
canActivate(req, context: ExecutionContext): boolean {
|
canActivate(context: ExecutionContext): boolean {
|
||||||
const { parent, handler } = context;
|
const roles = this.reflector.get<string[]>('roles', context.getHandler());
|
||||||
const roles = this.reflector.get<string[]>('roles', handler);
|
|
||||||
if (!roles) {
|
if (!roles) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const user = req.user;
|
const request = context.switchToHttp().getRequest();
|
||||||
|
const user = request.user;
|
||||||
const hasRole = () =>
|
const hasRole = () =>
|
||||||
!!user.roles.find(role => !!roles.find(item => item === role));
|
!!user.roles.find(role => !!roles.find(item => item === role));
|
||||||
return user && user.roles && hasRole();
|
return user && user.roles && hasRole();
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Injectable, NestInterceptor, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { of } from 'rxjs/observable/of';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export abstract class CacheInterceptor implements NestInterceptor {
|
|
||||||
protected abstract readonly isCached: () => boolean;
|
|
||||||
|
|
||||||
intercept(
|
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
|
||||||
stream$: Observable<any>,
|
|
||||||
): Observable<any> {
|
|
||||||
if (this.isCached()) {
|
|
||||||
return of([]);
|
|
||||||
}
|
|
||||||
return stream$;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,6 @@ import { _throw } from 'rxjs/observable/throw';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExceptionInterceptor implements NestInterceptor {
|
export class ExceptionInterceptor implements NestInterceptor {
|
||||||
intercept(
|
intercept(
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
stream$: Observable<any>,
|
stream$: Observable<any>,
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { tap } from 'rxjs/operators';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class LoggingInterceptor implements NestInterceptor {
|
export class LoggingInterceptor implements NestInterceptor {
|
||||||
intercept(
|
intercept(
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
stream$: Observable<any>,
|
stream$: Observable<any>,
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { mixin } from '@nestjs/common';
|
|
||||||
import { CacheInterceptor } from './cache.interceptor';
|
|
||||||
|
|
||||||
export function mixinCacheInterceptor(isCached: () => boolean) {
|
|
||||||
return mixin(
|
|
||||||
class extends CacheInterceptor {
|
|
||||||
protected readonly isCached = isCached;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ import { map } from 'rxjs/operators';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class TransformInterceptor implements NestInterceptor {
|
export class TransformInterceptor implements NestInterceptor {
|
||||||
intercept(
|
intercept(
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
stream$: Observable<any>,
|
stream$: Observable<any>,
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {
|
|||||||
PipeTransform,
|
PipeTransform,
|
||||||
Pipe,
|
Pipe,
|
||||||
ArgumentMetadata,
|
ArgumentMetadata,
|
||||||
HttpStatus,
|
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
|
||||||
@Pipe()
|
@Pipe()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
import { ApplicationModule } from './app.module';
|
import { ApplicationModule } from './app.module';
|
||||||
import { ValidationPipe } from './common/pipes/validation.pipe';
|
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(ApplicationModule);
|
const app = await NestFactory.create(ApplicationModule);
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
<script src="socket.io.js"></script>
|
<head>
|
||||||
<script>
|
<script src="socket.io.js"></script>
|
||||||
const socket = io('http://localhost:3000');
|
<script>
|
||||||
socket.on('connect', function() {
|
const socket = io('http://localhost:3000');
|
||||||
console.log('Connected');
|
socket.on('connect', function () {
|
||||||
socket.emit('events', { test: 'test' });
|
console.log('Connected');
|
||||||
});
|
socket.emit('events', { test: 'test' });
|
||||||
socket.on('events', function(data) {
|
});
|
||||||
console.log('event', data);
|
socket.on('events', function (data) {
|
||||||
});
|
console.log('event', data);
|
||||||
socket.on('exception', function(data) {
|
});
|
||||||
console.log('event', data);
|
socket.on('exception', function (data) {
|
||||||
});
|
console.log('event', data);
|
||||||
socket.on('disconnect', function() {
|
});
|
||||||
console.log('Disconnected');
|
socket.on('disconnect', function () {
|
||||||
});
|
console.log('Disconnected');
|
||||||
</script>
|
});
|
||||||
</head>
|
</script>
|
||||||
<body></body>
|
</head>
|
||||||
|
|
||||||
|
<body></body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script>
|
|
||||||
const socket = new WebSocket('ws://localhost:81');
|
|
||||||
socket.onopen = function() {
|
|
||||||
console.log('Connected');
|
|
||||||
socket.send(JSON.stringify({
|
|
||||||
type: 'events',
|
|
||||||
message: 'test',
|
|
||||||
}));
|
|
||||||
socket.onmessage = function(data) {
|
|
||||||
console.log(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body></body>
|
|
||||||
</html>
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import * as WebSocket from 'ws';
|
|
||||||
import { WebSocketAdapter } from '@nestjs/common';
|
|
||||||
import { MessageMappingProperties } from '@nestjs/websockets';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import 'rxjs/add/observable/fromEvent';
|
|
||||||
import 'rxjs/add/observable/empty';
|
|
||||||
import 'rxjs/add/operator/switchMap';
|
|
||||||
import 'rxjs/add/operator/filter';
|
|
||||||
|
|
||||||
export class WsAdapter implements WebSocketAdapter {
|
|
||||||
public create(port: number) {
|
|
||||||
return new WebSocket.Server({ port });
|
|
||||||
}
|
|
||||||
|
|
||||||
public bindClientConnect(server, callback: (...args: any[]) => void) {
|
|
||||||
server.on('connection', callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bindMessageHandlers(
|
|
||||||
client: WebSocket,
|
|
||||||
handlers: MessageMappingProperties[],
|
|
||||||
process: (data: any) => Observable<any>,
|
|
||||||
) {
|
|
||||||
Observable.fromEvent(client, 'message')
|
|
||||||
.switchMap(buffer => this.bindMessageHandler(buffer, handlers, process))
|
|
||||||
.filter(result => !!result)
|
|
||||||
.subscribe(response => client.send(JSON.stringify(response)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public bindMessageHandler(
|
|
||||||
buffer,
|
|
||||||
handlers: MessageMappingProperties[],
|
|
||||||
process: (data: any) => Observable<any>,
|
|
||||||
): Observable<any> {
|
|
||||||
const data = JSON.parse(buffer.data);
|
|
||||||
const messageHandler = handlers.find(
|
|
||||||
handler => handler.message === data.type,
|
|
||||||
);
|
|
||||||
if (!messageHandler) {
|
|
||||||
return Observable.empty();
|
|
||||||
}
|
|
||||||
const { callback } = messageHandler;
|
|
||||||
return process(callback(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { ReflectMetadata } from '@nestjs/common';
|
|
||||||
|
|
||||||
export const Roles = (...roles: string[]) => ReflectMetadata('roles', roles);
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { Catch, WsExceptionFilter } from '@nestjs/common';
|
|
||||||
import { WsException } from '@nestjs/websockets';
|
|
||||||
|
|
||||||
@Catch(WsException)
|
|
||||||
export class ExceptionFilter implements WsExceptionFilter {
|
|
||||||
catch(exception: WsException, client) {
|
|
||||||
client.emit('exception', {
|
|
||||||
status: 'error',
|
|
||||||
message: `It's a message from the exception filter`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { Reflector } from '@nestjs/core';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class RolesGuard implements CanActivate {
|
|
||||||
constructor(private readonly reflector: Reflector) {}
|
|
||||||
|
|
||||||
canActivate(data, context: ExecutionContext): boolean {
|
|
||||||
const { parent, handler } = context;
|
|
||||||
const roles = this.reflector.get<string[]>('roles', handler);
|
|
||||||
if (!roles) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = data.user;
|
|
||||||
const hasRole = () =>
|
|
||||||
!!user.roles.find(role => !!roles.find(item => item === role));
|
|
||||||
return user && user.roles && hasRole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Injectable, NestInterceptor, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import 'rxjs/add/observable/of';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CacheInterceptor implements NestInterceptor {
|
|
||||||
intercept(
|
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
|
||||||
stream$: Observable<any>,
|
|
||||||
): Observable<any> {
|
|
||||||
const isCached = true;
|
|
||||||
if (isCached) {
|
|
||||||
return Observable.of([]);
|
|
||||||
}
|
|
||||||
return stream$;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { Injectable, NestInterceptor, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import 'rxjs/add/operator/do';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class LoggingInterceptor implements NestInterceptor {
|
|
||||||
intercept(
|
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
|
||||||
stream$: Observable<any>,
|
|
||||||
): Observable<any> {
|
|
||||||
console.log('Before...');
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
return stream$.do(() => console.log(`After... ${Date.now() - now}ms`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Injectable, NestInterceptor, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import 'rxjs/add/operator/map';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TransformInterceptor implements NestInterceptor {
|
|
||||||
intercept(
|
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
|
||||||
stream$: Observable<any>,
|
|
||||||
): Observable<any> {
|
|
||||||
return stream$.map(data => ({ data }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import {
|
|
||||||
PipeTransform,
|
|
||||||
Pipe,
|
|
||||||
ArgumentMetadata,
|
|
||||||
HttpStatus,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { validate } from 'class-validator';
|
|
||||||
import { plainToClass } from 'class-transformer';
|
|
||||||
import { WsException } from '@nestjs/websockets';
|
|
||||||
|
|
||||||
@Pipe()
|
|
||||||
export class ValidationPipe implements PipeTransform<any> {
|
|
||||||
async transform(value, metadata: ArgumentMetadata) {
|
|
||||||
const { metatype } = metadata;
|
|
||||||
if (!metatype || !this.toValidate(metatype)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
const object = plainToClass(metatype, value);
|
|
||||||
const errors = await validate(object);
|
|
||||||
if (errors.length > 0) {
|
|
||||||
throw new WsException('Validation failed');
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private toValidate(metatype): boolean {
|
|
||||||
const types = [String, Boolean, Number, Array, Object];
|
|
||||||
return !types.find(type => metatype === type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import * as amqp from 'amqplib';
|
|
||||||
import { ClientProxy } from '@nestjs/microservices';
|
|
||||||
|
|
||||||
export class RabbitMQClient extends ClientProxy {
|
|
||||||
constructor(private readonly host: string, private readonly queue: string) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async sendSingleMessage(
|
|
||||||
messageObj,
|
|
||||||
callback: (err, result, disposed?: boolean) => void,
|
|
||||||
) {
|
|
||||||
const server = await amqp.connect(this.host);
|
|
||||||
const channel = await server.createChannel();
|
|
||||||
const { sub, pub } = this.getQueues();
|
|
||||||
|
|
||||||
channel.assertQueue(sub, { durable: false });
|
|
||||||
channel.assertQueue(pub, { durable: false });
|
|
||||||
|
|
||||||
channel.consume(
|
|
||||||
pub,
|
|
||||||
message => this.handleMessage(message, server, callback),
|
|
||||||
{ noAck: true },
|
|
||||||
);
|
|
||||||
channel.sendToQueue(sub, Buffer.from(JSON.stringify(messageObj)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleMessage(
|
|
||||||
message,
|
|
||||||
server,
|
|
||||||
callback: (err, result, disposed?: boolean) => void,
|
|
||||||
) {
|
|
||||||
const { content } = message;
|
|
||||||
const { err, response, disposed } = JSON.parse(content.toString());
|
|
||||||
if (disposed) {
|
|
||||||
server.close();
|
|
||||||
}
|
|
||||||
callback(err, response, disposed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getQueues() {
|
|
||||||
return { pub: `${this.queue}_pub`, sub: `${this.queue}_sub` };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import * as amqp from 'amqplib';
|
|
||||||
import { Server, CustomTransportStrategy } from '@nestjs/microservices';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
|
|
||||||
export class RabbitMQServer extends Server implements CustomTransportStrategy {
|
|
||||||
private server: amqp.Connection;
|
|
||||||
private channel: amqp.Channel;
|
|
||||||
|
|
||||||
constructor(private readonly host: string, private readonly queue: string) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async listen(callback: () => void) {
|
|
||||||
await this.init();
|
|
||||||
this.channel.consume(`${this.queue}_sub`, this.handleMessage.bind(this), {
|
|
||||||
noAck: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public close() {
|
|
||||||
this.channel && this.channel.close();
|
|
||||||
this.server && this.server.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleMessage(message) {
|
|
||||||
const { content } = message;
|
|
||||||
const messageObj = JSON.parse(content.toString());
|
|
||||||
const pattern = JSON.stringify(messageObj.pattern);
|
|
||||||
|
|
||||||
const handler = this.getHandlerByPattern(pattern);
|
|
||||||
if (!handler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const response$ = this.transformToObservable(
|
|
||||||
await handler(messageObj.data),
|
|
||||||
) as Observable<any>;
|
|
||||||
response$ && this.send(response$, data => this.sendMessage(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private sendMessage(message) {
|
|
||||||
const buffer = Buffer.from(JSON.stringify(message));
|
|
||||||
this.channel.sendToQueue(`${this.queue}_pub`, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async init() {
|
|
||||||
this.server = await amqp.connect(this.host);
|
|
||||||
this.channel = await this.server.createChannel();
|
|
||||||
this.channel.assertQueue(`${this.queue}_sub`, { durable: false });
|
|
||||||
this.channel.assertQueue(`${this.queue}_pub`, { durable: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import { ReflectMetadata } from '@nestjs/common';
|
|
||||||
|
|
||||||
export const Roles = (...roles: string[]) => ReflectMetadata('roles', roles);
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { Reflector } from '@nestjs/core';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class RolesGuard implements CanActivate {
|
|
||||||
constructor(private readonly reflector: Reflector) {}
|
|
||||||
|
|
||||||
canActivate(data, context: ExecutionContext): boolean {
|
|
||||||
const { parent, handler } = context;
|
|
||||||
const roles = this.reflector.get<string[]>('roles', handler);
|
|
||||||
if (!roles) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const user = data.user;
|
|
||||||
const hasRole = () =>
|
|
||||||
!!user.roles.find(role => !!roles.find(item => item === role));
|
|
||||||
return user && user.roles && hasRole();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Injectable, NestInterceptor, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { of } from 'rxjs/observable/of';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CacheInterceptor implements NestInterceptor {
|
|
||||||
intercept(
|
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
|
||||||
stream$: Observable<any>,
|
|
||||||
): Observable<any> {
|
|
||||||
const isCached = true;
|
|
||||||
if (isCached) {
|
|
||||||
return of([]);
|
|
||||||
}
|
|
||||||
return stream$;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,6 @@ import { tap } from 'rxjs/operators';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class LoggingInterceptor implements NestInterceptor {
|
export class LoggingInterceptor implements NestInterceptor {
|
||||||
intercept(
|
intercept(
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
stream$: Observable<any>,
|
stream$: Observable<any>,
|
||||||
): Observable<any> {
|
): Observable<any> {
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { Injectable, NestInterceptor, ExecutionContext } from '@nestjs/common';
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TransformInterceptor implements NestInterceptor {
|
|
||||||
intercept(
|
|
||||||
dataOrRequest,
|
|
||||||
context: ExecutionContext,
|
|
||||||
stream$: Observable<any>,
|
|
||||||
): Observable<any> {
|
|
||||||
return stream$.pipe(map(data => ({ data })));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { PipeTransform, Pipe, ArgumentMetadata } from '@nestjs/common';
|
|
||||||
import { validate } from 'class-validator';
|
|
||||||
import { plainToClass } from 'class-transformer';
|
|
||||||
import { RpcException } from '@nestjs/microservices';
|
|
||||||
|
|
||||||
@Pipe()
|
|
||||||
export class ValidationPipe implements PipeTransform<any> {
|
|
||||||
async transform(value, metadata: ArgumentMetadata) {
|
|
||||||
const { metatype } = metadata;
|
|
||||||
if (!metatype || !this.toValidate(metatype)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
const object = plainToClass(metatype, value);
|
|
||||||
const errors = await validate(object);
|
|
||||||
if (errors.length > 0) {
|
|
||||||
throw new RpcException('Validation failed');
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private toValidate(metatype): boolean {
|
|
||||||
const types = [String, Boolean, Number, Array, Object];
|
|
||||||
return !types.find(type => metatype === type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,19 +3,22 @@ import { Transport } from '@nestjs/microservices';
|
|||||||
import { ApplicationModule } from './app.module';
|
import { ApplicationModule } from './app.module';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
/*const app = await NestFactory.createMicroservice(ApplicationModule, {
|
/**
|
||||||
retryAttempts: 5,
|
* Hybrid application (HTTP + TCP)
|
||||||
retryDelay: 3000,
|
* Switch to basic microservice with NestFactory.createMicroservice():
|
||||||
transport: Transport.REDIS,
|
*
|
||||||
} as any);
|
* const app = await NestFactory.createMicroservice(ApplicationModule, {
|
||||||
await app.listenAsync();*/
|
* transport: Transport.TCP,
|
||||||
/** Hybrid application (HTTP + (n)Microservices)*/
|
* options: { retryAttempts: 5, retryDelay: 3000 },
|
||||||
|
* });
|
||||||
|
* await app.listenAsync();
|
||||||
|
*
|
||||||
|
*/
|
||||||
const app = await NestFactory.create(ApplicationModule);
|
const app = await NestFactory.create(ApplicationModule);
|
||||||
app.connectMicroservice({
|
app.connectMicroservice({
|
||||||
retryAttempts: 5,
|
transport: Transport.TCP,
|
||||||
retryDelay: 3000,
|
options: { retryAttempts: 5, retryDelay: 3000 },
|
||||||
transport: Transport.TCP,
|
});
|
||||||
} as any);
|
|
||||||
|
|
||||||
await app.startAllMicroservicesAsync();
|
await app.startAllMicroservicesAsync();
|
||||||
await app.listen(3001);
|
await app.listen(3001);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Controller, Get, UseInterceptors } from '@nestjs/common';
|
import { Controller, Get } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
ClientProxy,
|
ClientProxy,
|
||||||
Client,
|
Client,
|
||||||
@@ -6,10 +6,8 @@ import {
|
|||||||
MessagePattern,
|
MessagePattern,
|
||||||
} from '@nestjs/microservices';
|
} from '@nestjs/microservices';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { LoggingInterceptor } from '../common/interceptors/logging.interceptor';
|
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
@UseInterceptors(LoggingInterceptor)
|
|
||||||
export class MathController {
|
export class MathController {
|
||||||
@Client({ transport: Transport.TCP })
|
@Client({ transport: Transport.TCP })
|
||||||
client: ClientProxy;
|
client: ClientProxy;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"description": "Nest TypeScript starter repository",
|
"description": "Nest TypeScript starter repository",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "ts-node src/main",
|
"start": "ts-node src/main.ts",
|
||||||
"prestart:prod": "tsc",
|
"prestart:prod": "tsc",
|
||||||
"start:prod": "node dist/main.js"
|
"start:prod": "node dist/main.js"
|
||||||
},
|
},
|
||||||
@@ -14,11 +14,9 @@
|
|||||||
"@nestjs/microservices": "^5.0.0",
|
"@nestjs/microservices": "^5.0.0",
|
||||||
"@nestjs/testing": "^5.0.0",
|
"@nestjs/testing": "^5.0.0",
|
||||||
"@nestjs/websockets": "^5.0.0",
|
"@nestjs/websockets": "^5.0.0",
|
||||||
"@types/passport-jwt": "^2.0.24",
|
"class-transformer": "^0.1.7",
|
||||||
"jsonwebtoken": "^8.0.1",
|
"class-validator": "^0.7.2",
|
||||||
"passport": "^0.4.0",
|
"grpc": "^1.10.0",
|
||||||
"passport-jwt": "^3.0.0",
|
|
||||||
"redis": "^2.7.1",
|
|
||||||
"reflect-metadata": "^0.1.10",
|
"reflect-metadata": "^0.1.10",
|
||||||
"rxjs": "^5.5.6",
|
"rxjs": "^5.5.6",
|
||||||
"typescript": "^2.4.2"
|
"typescript": "^2.4.2"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { AuthModule } from './auth/auth.module';
|
import { HeroModule } from './hero/hero.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [AuthModule],
|
imports: [HeroModule],
|
||||||
})
|
})
|
||||||
export class ApplicationModule {}
|
export class ApplicationModule {}
|
||||||
39
sample/04-grpc/src/hero/hero.controller.ts
Normal file
39
sample/04-grpc/src/hero/hero.controller.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ClientProxy,
|
||||||
|
Client,
|
||||||
|
Transport,
|
||||||
|
MessagePattern,
|
||||||
|
GrpcRoute,
|
||||||
|
ClientGrpc,
|
||||||
|
} from '@nestjs/microservices';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
interface HeroService {
|
||||||
|
FindOne(data: { id: number }): Observable<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class HeroController {
|
||||||
|
@Client({
|
||||||
|
transport: Transport.GRPC,
|
||||||
|
options: {
|
||||||
|
package: 'hero',
|
||||||
|
protoPath: join(__dirname, './hero.proto'),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
client: ClientGrpc;
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
call(): Observable<any> {
|
||||||
|
const heroService = this.client.getService<HeroService>('HeroService');
|
||||||
|
return heroService.FindOne({ id: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
@GrpcRoute('HeroService', 'FindOne')
|
||||||
|
findOne(data: { id: number }): any {
|
||||||
|
const items = [{ id: 1, name: 'John' }, { id: 2, name: 'Doe' }];
|
||||||
|
return items.find(({ id }) => id === data.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
sample/04-grpc/src/hero/hero.module.ts
Normal file
7
sample/04-grpc/src/hero/hero.module.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { HeroController } from './hero.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [HeroController],
|
||||||
|
})
|
||||||
|
export class HeroModule {}
|
||||||
16
sample/04-grpc/src/hero/hero.proto
Normal file
16
sample/04-grpc/src/hero/hero.proto
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package hero;
|
||||||
|
|
||||||
|
service HeroService {
|
||||||
|
rpc FindOne (HeroById) returns (Hero) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
message HeroById {
|
||||||
|
int32 id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Hero {
|
||||||
|
int32 id = 1;
|
||||||
|
string name = 2;
|
||||||
|
}
|
||||||
32
sample/04-grpc/src/main.ts
Normal file
32
sample/04-grpc/src/main.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { Transport } from '@nestjs/microservices';
|
||||||
|
import { ApplicationModule } from './app.module';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
/**
|
||||||
|
* Hybrid application (HTTP + GRPC)
|
||||||
|
* Switch to basic microservice with NestFactory.createMicroservice():
|
||||||
|
*
|
||||||
|
* const app = await NestFactory.createMicroservice(ApplicationModule, {
|
||||||
|
* transport: Transport.GRPC,
|
||||||
|
* options: {
|
||||||
|
* package: 'hero',
|
||||||
|
* protoPath: join(__dirname, './hero/hero.proto'),
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* await app.listenAsync();
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const app = await NestFactory.create(ApplicationModule);
|
||||||
|
app.connectMicroservice({
|
||||||
|
transport: Transport.GRPC,
|
||||||
|
options: {
|
||||||
|
package: 'hero',
|
||||||
|
protoPath: join(__dirname, './hero/hero.proto'),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await app.startAllMicroservicesAsync();
|
||||||
|
await app.listen(3001);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { CoreModule } from './core/core.module';
|
|
||||||
import { FeatureModule } from './feature/feature.module';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [FeatureModule],
|
|
||||||
})
|
|
||||||
export class ApplicationModule {}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { Module, SingleScope } from '@nestjs/common';
|
|
||||||
import { CommonService } from './common.service';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
providers: [CommonService],
|
|
||||||
exports: [CommonService],
|
|
||||||
})
|
|
||||||
export class CommonModule {}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { CoreService } from '../core/core.service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CommonService {
|
|
||||||
constructor(private readonly coreService: CoreService) {
|
|
||||||
console.log('CommonService', coreService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { CommonService } from '../common/common.service';
|
|
||||||
import { CoreService } from './core.service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ContextService {
|
|
||||||
constructor(private readonly commonService: CoreService) {
|
|
||||||
console.log('ContextService', commonService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user