mirror of
https://github.com/nestjs/nest.git
synced 2026-02-23 15:52:50 +00:00
Compare commits
455 Commits
adworacz-v
...
Tony133-ch
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fac9c9bf2 | ||
|
|
dd58e42501 | ||
|
|
2362ade438 | ||
|
|
f11181071e | ||
|
|
11ac94e23e | ||
|
|
cd62e31bcf | ||
|
|
34143d9e79 | ||
|
|
79cccd0574 | ||
|
|
71e3143d90 | ||
|
|
2eaee87f09 | ||
|
|
7ca14a2ed4 | ||
|
|
b7d92b2b2a | ||
|
|
d38a7d894f | ||
|
|
cf42ed5b8d | ||
|
|
72c046dec7 | ||
|
|
1e8966b603 | ||
|
|
40e2755c82 | ||
|
|
64e58b750c | ||
|
|
a21c75c4f7 | ||
|
|
61bcb7fe39 | ||
|
|
ddd95b810f | ||
|
|
707a3e069f | ||
|
|
66da12aecf | ||
|
|
1f5a5b4f2e | ||
|
|
be1a8c33bd | ||
|
|
e3e3ef5cc5 | ||
|
|
fc95619e4d | ||
|
|
19b55e9a37 | ||
|
|
ca967b809e | ||
|
|
206579242a | ||
|
|
ddd24f970d | ||
|
|
28afaa27dc | ||
|
|
bad85d9172 | ||
|
|
dde23b1a2b | ||
|
|
4ca738dddf | ||
|
|
5464d72738 | ||
|
|
cfe1191d22 | ||
|
|
20870b1d41 | ||
|
|
90af9590f5 | ||
|
|
6547f922fb | ||
|
|
a439055646 | ||
|
|
842b7cb776 | ||
|
|
7cea25f6b7 | ||
|
|
5c904a622f | ||
|
|
15737e0622 | ||
|
|
46ec9770b9 | ||
|
|
5467777e9b | ||
|
|
07e17a4ca9 | ||
|
|
21bffef69b | ||
|
|
353854e88d | ||
|
|
73f45db25e | ||
|
|
2c1bde5ddc | ||
|
|
c10d17c71c | ||
|
|
956d7f828b | ||
|
|
d30d990ca9 | ||
|
|
68cf225b28 | ||
|
|
df2e404c93 | ||
|
|
26fd5a7d41 | ||
|
|
63ed53f1cb | ||
|
|
1e56057251 | ||
|
|
ce2a4590b4 | ||
|
|
23760fe43c | ||
|
|
98f4ac8fae | ||
|
|
b5a1e7b427 | ||
|
|
521f5867e9 | ||
|
|
4064b96196 | ||
|
|
cc2a242986 | ||
|
|
1c2d77cba1 | ||
|
|
7f185052f9 | ||
|
|
81392cb480 | ||
|
|
c8358b01f3 | ||
|
|
6f5986d19e | ||
|
|
dea497a66f | ||
|
|
f0a8ac32bc | ||
|
|
1c0bec8f95 | ||
|
|
dee8396f2d | ||
|
|
532b5e8c65 | ||
|
|
97bfb4af94 | ||
|
|
3be31dde46 | ||
|
|
ff2de15bf1 | ||
|
|
654ba2b6f0 | ||
|
|
b7370b2236 | ||
|
|
3c04cbb992 | ||
|
|
24e67a5fa2 | ||
|
|
d14a944a9c | ||
|
|
002c11af4f | ||
|
|
ace4fe5556 | ||
|
|
c453522252 | ||
|
|
9dbc23b1ca | ||
|
|
b614b49f4c | ||
|
|
87a24633dd | ||
|
|
7f2ce42a0f | ||
|
|
d97e117269 | ||
|
|
e2715836f8 | ||
|
|
de28fa349b | ||
|
|
0fed555560 | ||
|
|
5376136979 | ||
|
|
e5565fc4c2 | ||
|
|
1d8374e007 | ||
|
|
0a077c94b4 | ||
|
|
cf37e61bc1 | ||
|
|
03ab77cb74 | ||
|
|
dafa1cf754 | ||
|
|
0a1c7e9768 | ||
|
|
44cd4573f9 | ||
|
|
ea4f9d0edc | ||
|
|
8f237751b3 | ||
|
|
6ded88c0c9 | ||
|
|
c77bef9e56 | ||
|
|
8584af1ac5 | ||
|
|
ae2cde105d | ||
|
|
2db088fa14 | ||
|
|
2f9f2edf89 | ||
|
|
74e3f5fff3 | ||
|
|
37f7b1cc3a | ||
|
|
0b137ff1c5 | ||
|
|
e7087adcb7 | ||
|
|
a3cfb1717a | ||
|
|
e0255e7a1e | ||
|
|
341f0518f9 | ||
|
|
2d8f838aea | ||
|
|
7d84e835f4 | ||
|
|
d1d1a16eb4 | ||
|
|
4d6216695d | ||
|
|
8d7d63daa4 | ||
|
|
37f580c0a2 | ||
|
|
9cd425cac7 | ||
|
|
3ceeb114af | ||
|
|
340dcaaef4 | ||
|
|
92bc4556f7 | ||
|
|
5eedf8a9b1 | ||
|
|
13f71afa60 | ||
|
|
e17dc7b4a8 | ||
|
|
bb38f328e3 | ||
|
|
d9eeda4d79 | ||
|
|
9ba4903419 | ||
|
|
7192d4de53 | ||
|
|
9cd4fefd3a | ||
|
|
84c4589497 | ||
|
|
f3a31c8ffd | ||
|
|
1aa7b946c6 | ||
|
|
38b0f8b69e | ||
|
|
df6e227da4 | ||
|
|
0b82026efc | ||
|
|
85c06f3a1e | ||
|
|
0d0a6c7657 | ||
|
|
581ba5aec6 | ||
|
|
6c7c1d63dc | ||
|
|
19b210337f | ||
|
|
6731aaf59b | ||
|
|
208390e98f | ||
|
|
0e75c7db31 | ||
|
|
fc19ec532d | ||
|
|
fae3431e81 | ||
|
|
a5eb9b3d82 | ||
|
|
73ddac2005 | ||
|
|
02d9f4941e | ||
|
|
d21c30f7bc | ||
|
|
82b019dae0 | ||
|
|
2574a79a8b | ||
|
|
87969fba5f | ||
|
|
55371752ba | ||
|
|
560379c166 | ||
|
|
c53f7f2cde | ||
|
|
b11959c979 | ||
|
|
9a8adbee88 | ||
|
|
7685993942 | ||
|
|
4ce5877b2e | ||
|
|
bc0a1dedf8 | ||
|
|
d51c7289a8 | ||
|
|
41850c0920 | ||
|
|
4fb646ec29 | ||
|
|
43089c5b28 | ||
|
|
d067144331 | ||
|
|
1f6fca5f55 | ||
|
|
8036e2a59b | ||
|
|
b1844b6be4 | ||
|
|
f14d2f2714 | ||
|
|
de044a3daf | ||
|
|
3d9bd7fcfa | ||
|
|
fd40c9bea4 | ||
|
|
e0a4ed39fe | ||
|
|
844bbbb6fc | ||
|
|
ef6c770bec | ||
|
|
1a1f39fa4f | ||
|
|
e80a14f447 | ||
|
|
d9d628d112 | ||
|
|
525f4f066c | ||
|
|
1089eedec4 | ||
|
|
a2d65f3d01 | ||
|
|
e55cad38d9 | ||
|
|
41bf2dc9bf | ||
|
|
c5c41495a2 | ||
|
|
eed06091d3 | ||
|
|
3601f1a171 | ||
|
|
a7ba3e0dc2 | ||
|
|
4c649edc2d | ||
|
|
acac12790c | ||
|
|
4177feabdc | ||
|
|
381fc42301 | ||
|
|
fe9c8d071c | ||
|
|
2d342a1b49 | ||
|
|
c7a72bfd33 | ||
|
|
65d7f1e8ea | ||
|
|
a615d70044 | ||
|
|
785db640aa | ||
|
|
625d3672d4 | ||
|
|
ffdefbf856 | ||
|
|
be6848dac9 | ||
|
|
45872ae2ea | ||
|
|
21e3293052 | ||
|
|
53d88e5952 | ||
|
|
efa45f6a45 | ||
|
|
d884e569f0 | ||
|
|
b389f5aa4e | ||
|
|
8864699539 | ||
|
|
97613abf26 | ||
|
|
30b6e8c494 | ||
|
|
9497c8a8ef | ||
|
|
8d96b09349 | ||
|
|
a3b2dde463 | ||
|
|
2308640395 | ||
|
|
215878ec59 | ||
|
|
fc5bd1dfe8 | ||
|
|
12c899f8b3 | ||
|
|
1622e9a6e9 | ||
|
|
5910bd60f9 | ||
|
|
b267c63012 | ||
|
|
926e06a211 | ||
|
|
faa32ab9a6 | ||
|
|
a2c9570f5e | ||
|
|
088111874e | ||
|
|
890d2beb47 | ||
|
|
5745b24608 | ||
|
|
43fa5bc4c5 | ||
|
|
e971856555 | ||
|
|
af0e4001ff | ||
|
|
123dd469ee | ||
|
|
e5e8252bcd | ||
|
|
4f91d6f4d4 | ||
|
|
9431b6cfe9 | ||
|
|
3a8b0ffe90 | ||
|
|
02d80be75e | ||
|
|
aab08839cc | ||
|
|
75188b79a3 | ||
|
|
d3cccbe4d5 | ||
|
|
31b5affe40 | ||
|
|
604a10b3b6 | ||
|
|
f23486f943 | ||
|
|
b1badde2ea | ||
|
|
048da5b823 | ||
|
|
a718cd9e3d | ||
|
|
72f6312822 | ||
|
|
03bc1a4875 | ||
|
|
dd00c82fdf | ||
|
|
3368e75377 | ||
|
|
65fb78a77d | ||
|
|
31001bbb87 | ||
|
|
9c04af69fb | ||
|
|
07dd969245 | ||
|
|
75fdb69599 | ||
|
|
da41efe3c1 | ||
|
|
1d1c558b23 | ||
|
|
53cf87de7d | ||
|
|
2694e87aa3 | ||
|
|
93fd9a6752 | ||
|
|
f1af65e92e | ||
|
|
e485ddf960 | ||
|
|
37607cdfae | ||
|
|
aabe1accee | ||
|
|
5ad8f2ba57 | ||
|
|
eb33fe5919 | ||
|
|
33cba79f55 | ||
|
|
43a0514edd | ||
|
|
60f09913af | ||
|
|
8b0632e6c7 | ||
|
|
c4887a7f44 | ||
|
|
d4420611f7 | ||
|
|
993faeb299 | ||
|
|
f4be93b3e6 | ||
|
|
33ac6add9f | ||
|
|
e1f67b9eac | ||
|
|
04b8567ce6 | ||
|
|
868771f018 | ||
|
|
5e21db79a3 | ||
|
|
2b42b410c6 | ||
|
|
0e1ee44276 | ||
|
|
cb17bbbbe4 | ||
|
|
34a4883aad | ||
|
|
40cfd37fb3 | ||
|
|
ae52d9651a | ||
|
|
18be1e1158 | ||
|
|
a1bebff55b | ||
|
|
0de707a997 | ||
|
|
061a19eac6 | ||
|
|
0ad71f844f | ||
|
|
120ad4ccdb | ||
|
|
0253e92d89 | ||
|
|
8c8db9f497 | ||
|
|
d581153c06 | ||
|
|
d74a8d637f | ||
|
|
ad7972b203 | ||
|
|
e9462d2712 | ||
|
|
276b28cdcb | ||
|
|
17d48f3cb4 | ||
|
|
f502bed31f | ||
|
|
bf583bc870 | ||
|
|
6c842b5ee6 | ||
|
|
b56be10e06 | ||
|
|
c23a9f7ef7 | ||
|
|
852770f4ed | ||
|
|
85a18b97e7 | ||
|
|
b83940ea50 | ||
|
|
faf1863cd3 | ||
|
|
a4ef8b85d8 | ||
|
|
5c1d8dbad9 | ||
|
|
e671a38b15 | ||
|
|
a83923425f | ||
|
|
f0988950f6 | ||
|
|
97d657408a | ||
|
|
460c2ebfe2 | ||
|
|
42c0353518 | ||
|
|
54ec050404 | ||
|
|
3776733960 | ||
|
|
abdde0aad5 | ||
|
|
6183d04ba9 | ||
|
|
8ea63376ee | ||
|
|
c8bd058757 | ||
|
|
f28a964200 | ||
|
|
4539568030 | ||
|
|
7e349178f9 | ||
|
|
b2fd422149 | ||
|
|
b0e568e929 | ||
|
|
a6e28f5953 | ||
|
|
c05fcfb2ca | ||
|
|
7d1124a42f | ||
|
|
0dc4f919f1 | ||
|
|
0990d4b270 | ||
|
|
8c705aa8da | ||
|
|
2f4e9d9293 | ||
|
|
1ae72e1fdb | ||
|
|
c7bdeada00 | ||
|
|
6fc31586bc | ||
|
|
ab9a1a54f6 | ||
|
|
f3530eee5e | ||
|
|
26b3d1d8d8 | ||
|
|
2d989b116f | ||
|
|
61fe8de7e7 | ||
|
|
8fa574453a | ||
|
|
4d9b4cc118 | ||
|
|
b71bf6b9df | ||
|
|
0efe425187 | ||
|
|
5ef23f1058 | ||
|
|
7d265c34e2 | ||
|
|
efc5b63ed0 | ||
|
|
9997006637 | ||
|
|
fc96eea2d7 | ||
|
|
c09f6debd1 | ||
|
|
a23f858011 | ||
|
|
50bb472ea3 | ||
|
|
674a09c175 | ||
|
|
34c09f5a61 | ||
|
|
76b1837886 | ||
|
|
3b2407bba7 | ||
|
|
a06ae58426 | ||
|
|
37d83eeb21 | ||
|
|
0b93404bef | ||
|
|
89d97652ca | ||
|
|
bf0768ed4b | ||
|
|
6071755cc1 | ||
|
|
f89de3f85a | ||
|
|
a05369ff98 | ||
|
|
4aa257a30e | ||
|
|
335a4dd48a | ||
|
|
f5051727ea | ||
|
|
4a6edc5e75 | ||
|
|
625bea70b2 | ||
|
|
5e52a7e85e | ||
|
|
6920399925 | ||
|
|
2d8db167df | ||
|
|
dbdc5963b2 | ||
|
|
1de8aac473 | ||
|
|
0fe67d5f90 | ||
|
|
ef70418627 | ||
|
|
b91803c671 | ||
|
|
e337f29954 | ||
|
|
4e41a72773 | ||
|
|
a5552c34e6 | ||
|
|
46ccebeda3 | ||
|
|
5feda61e17 | ||
|
|
a129ee137b | ||
|
|
4d9fb0d29a | ||
|
|
0be9c63bf0 | ||
|
|
d661fda872 | ||
|
|
c151efebcb | ||
|
|
795eaec304 | ||
|
|
48dca4ee22 | ||
|
|
c229a75c43 | ||
|
|
4d689ee356 | ||
|
|
77b7bd6905 | ||
|
|
5b24f81cd8 | ||
|
|
5038132633 | ||
|
|
94b39db8f5 | ||
|
|
db3654a891 | ||
|
|
46db4c4d4b | ||
|
|
788e11ff5e | ||
|
|
8f7b7a8014 | ||
|
|
f864d75b72 | ||
|
|
88565b417f | ||
|
|
2d7d28189e | ||
|
|
e5080b85ee | ||
|
|
cce221d726 | ||
|
|
2f73f70414 | ||
|
|
31d63d6859 | ||
|
|
285bd4b595 | ||
|
|
5f2e784a44 | ||
|
|
797e04ae26 | ||
|
|
7f9aa9cec0 | ||
|
|
07c9ce75ea | ||
|
|
acc96ddb3f | ||
|
|
080ad6fe40 | ||
|
|
4e7dd9a19e | ||
|
|
3147279ceb | ||
|
|
ec9537b3f3 | ||
|
|
94c2aca452 | ||
|
|
fb725658f1 | ||
|
|
4f33806c95 | ||
|
|
aeadc14dca | ||
|
|
0a90cbe45c | ||
|
|
61c5c0614f | ||
|
|
43a4770b74 | ||
|
|
8ce0cf1bc9 | ||
|
|
16531d47d9 | ||
|
|
7155a18089 | ||
|
|
2d3a3b4051 | ||
|
|
852fc91865 | ||
|
|
668ecd1cf8 | ||
|
|
91178c8a8a | ||
|
|
4ae2ef54d4 | ||
|
|
bac34473d0 | ||
|
|
e8abf50812 | ||
|
|
5d146afa0b | ||
|
|
ce96a0ed13 | ||
|
|
111d84e25e | ||
|
|
033e1ffe2d | ||
|
|
14a9ed0ea2 | ||
|
|
c7d8935632 | ||
|
|
75d4a9b6ee | ||
|
|
9c0cdd2950 | ||
|
|
7cc81067fe | ||
|
|
d32e8dd582 | ||
|
|
472b545880 | ||
|
|
1741f2106a | ||
|
|
3827736d96 | ||
|
|
a364ced0ac |
@@ -65,11 +65,6 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
|
||||
test_node_10:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
|
||||
test_node_14:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
@@ -174,9 +169,6 @@ workflows:
|
||||
- test_node_12:
|
||||
requires:
|
||||
- build
|
||||
- test_node_10:
|
||||
requires:
|
||||
- build
|
||||
- lint:
|
||||
requires:
|
||||
- build
|
||||
|
||||
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -5,3 +5,8 @@ updates:
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -29,7 +29,9 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
queries: +security-extended
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@@ -37,7 +39,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -51,4 +53,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@ node_modules/
|
||||
/.idea
|
||||
/.awcache
|
||||
/.vscode
|
||||
/.devcontainer
|
||||
*.code-workspace
|
||||
|
||||
# bundle
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
module.exports = {
|
||||
mochaHooks: {
|
||||
beforeAll: function() {
|
||||
require('reflect-metadata');
|
||||
}
|
||||
}
|
||||
};
|
||||
11
Readme.md
11
Readme.md
@@ -64,17 +64,20 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://careers.labster.com/departments/platform" target="_blank"><img src="https://nestjs.com/img/labster-logo.png" width="170" valign="middle" /></a></td><td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="150" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></</tr></table>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="110" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td></</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
|
||||
7
hooks/mocha-init-hook.ts
Normal file
7
hooks/mocha-init-hook.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const mochaHooks = (): Mocha.RootHookObject => {
|
||||
return {
|
||||
async beforeAll(this: Mocha.Context) {
|
||||
await import('reflect-metadata');
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -17,13 +17,15 @@ services:
|
||||
restart: always
|
||||
mqtt:
|
||||
container_name: test-mqtt
|
||||
image: toke/mosquitto
|
||||
image: eclipse-mosquitto
|
||||
volumes:
|
||||
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
|
||||
ports:
|
||||
- "1883:1883"
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
mysql:
|
||||
image: mysql:8.0.27
|
||||
image: mysql:8.0.28
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: test
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('Disconnected client', () => {
|
||||
.send({
|
||||
transport: Transport.REDIS,
|
||||
options: {
|
||||
url: 'redis://localhost:3333',
|
||||
port: '3333',
|
||||
},
|
||||
})
|
||||
.expect(408);
|
||||
|
||||
@@ -124,6 +124,44 @@ describe('MQTT transport', () => {
|
||||
.expect(201, '15');
|
||||
});
|
||||
|
||||
it(`/POST (shared wildcard EVENT #)`, done => {
|
||||
request(server)
|
||||
.post('/shared-wildcard-event')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.end(() => {
|
||||
setTimeout(() => {
|
||||
expect(MqttController.IS_SHARED_WILDCARD_EVENT_RECEIVED).to.be.true;
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
it(`/POST (shared wildcard MESSAGE #)`, () => {
|
||||
return request(server)
|
||||
.post('/shared-wildcard-message')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(201, '15');
|
||||
});
|
||||
|
||||
it(`/POST (shared wildcard EVENT +)`, done => {
|
||||
request(server)
|
||||
.post('/shared-wildcard-event2')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.end(() => {
|
||||
setTimeout(() => {
|
||||
expect(MqttController.IS_SHARED_WILDCARD2_EVENT_RECEIVED).to.be.true;
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
it(`/POST (shared wildcard MESSAGE +)`, () => {
|
||||
return request(server)
|
||||
.post('/shared-wildcard-message2')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(201, '15');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -77,6 +77,13 @@ describe('RabbitMQ transport', () => {
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (multiple-urls)`, () => {
|
||||
return request(server)
|
||||
.post('/multiple-urls')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200, '15');
|
||||
}).timeout(10000);
|
||||
|
||||
it(`/POST (event notification)`, done => {
|
||||
request(server)
|
||||
.post('/notify')
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
Controller,
|
||||
InternalServerErrorException,
|
||||
Post,
|
||||
RequestTimeoutException,
|
||||
RequestTimeoutException
|
||||
} from '@nestjs/common';
|
||||
import { ClientProxyFactory } from '@nestjs/microservices';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
@@ -24,7 +24,8 @@ export class DisconnectedClientController {
|
||||
return throwError(() =>
|
||||
code === 'ECONNREFUSED' ||
|
||||
code === 'CONN_ERR' ||
|
||||
code === 'CONNECTION_REFUSED'
|
||||
code === 'CONNECTION_REFUSED' ||
|
||||
error.message === 'Connection is closed.'
|
||||
? new RequestTimeoutException('ECONNREFUSED')
|
||||
: new InternalServerErrorException(),
|
||||
);
|
||||
|
||||
@@ -18,6 +18,8 @@ export class MqttController {
|
||||
static IS_NOTIFIED = false;
|
||||
static IS_WILDCARD_EVENT_RECEIVED = false;
|
||||
static IS_WILDCARD2_EVENT_RECEIVED = false;
|
||||
static IS_SHARED_WILDCARD_EVENT_RECEIVED = false;
|
||||
static IS_SHARED_WILDCARD2_EVENT_RECEIVED = false;
|
||||
|
||||
@Client({ transport: Transport.MQTT })
|
||||
client: ClientProxy;
|
||||
@@ -109,6 +111,32 @@ export class MqttController {
|
||||
};
|
||||
}
|
||||
|
||||
@Post('shared-wildcard-event')
|
||||
async sendSharedWildcardEvent(): Promise<any> {
|
||||
return this.client.emit<number>('shared-wildcard-event/test', true);
|
||||
}
|
||||
|
||||
@Post('shared-wildcard-message')
|
||||
async sendSharedWildcardMessage(
|
||||
@Body() data: number[],
|
||||
): Promise<Observable<number>> {
|
||||
await this.client.connect();
|
||||
return this.client.send<number>('shared-wildcard-message/test', data);
|
||||
}
|
||||
|
||||
@Post('shared-wildcard-event2')
|
||||
async sendSharedWildcardEvent2(): Promise<any> {
|
||||
return this.client.emit<number>('shared-wildcard-event2/test/test', true);
|
||||
}
|
||||
|
||||
@Post('shared-wildcard-message2')
|
||||
async sendSharedWildcardMessage2(
|
||||
@Body() data: number[],
|
||||
): Promise<Observable<number>> {
|
||||
await this.client.connect();
|
||||
return this.client.send<number>('shared-wildcard-message2/test/test', data);
|
||||
}
|
||||
|
||||
@MessagePattern('wildcard-message/#')
|
||||
wildcardMessageHandler(data: number[]): number {
|
||||
if ((data as any).response) {
|
||||
@@ -156,4 +184,27 @@ export class MqttController {
|
||||
streaming(data: number[]): Observable<number> {
|
||||
return from(data);
|
||||
}
|
||||
|
||||
@MessagePattern('$share/test-group/shared-wildcard-message/#')
|
||||
sharedWildcardMessageHandler(data: number[]): number {
|
||||
if ((data as any).response) {
|
||||
return;
|
||||
}
|
||||
return (data || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@EventPattern('$share/test-group/shared-wildcard-event/#')
|
||||
sharedWildcardEventHandler(data: boolean) {
|
||||
MqttController.IS_SHARED_WILDCARD_EVENT_RECEIVED = data;
|
||||
}
|
||||
|
||||
@MessagePattern('$share/test-group/shared-wildcard-message2/+/test')
|
||||
sharedWildcardMessageHandler2(data: number[]): number {
|
||||
return (data || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@EventPattern('$share/test-group/shared-wildcard-event2/+/test')
|
||||
sharedWildcardEventHandler2(data: boolean) {
|
||||
MqttController.IS_SHARED_WILDCARD2_EVENT_RECEIVED = data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,21 @@ export class RMQController {
|
||||
.reduce(async (a, b) => (await a) && b);
|
||||
}
|
||||
|
||||
@Post('multiple-urls')
|
||||
@HttpCode(200)
|
||||
multipleUrls(@Body() data: number[]) {
|
||||
const clientWithMultipleUrls = ClientProxyFactory.create({
|
||||
transport: Transport.RMQ,
|
||||
options: {
|
||||
urls: [`amqp://localhost:5671`, `amqp://localhost:5672`],
|
||||
queue: 'test',
|
||||
queueOptions: { durable: false },
|
||||
socketOptions: { noDelay: true },
|
||||
},
|
||||
});
|
||||
return clientWithMultipleUrls.send<number>({ cmd: 'multiple-urls' }, data);
|
||||
}
|
||||
|
||||
@Post('record-builder-duplex')
|
||||
@HttpCode(200)
|
||||
useRecordBuilderDuplex(@Body() data: Record<string, any>) {
|
||||
@@ -109,6 +124,11 @@ export class RMQController {
|
||||
return from(data);
|
||||
}
|
||||
|
||||
@MessagePattern({ cmd: 'multiple-urls' })
|
||||
handleMultipleUrls(data: number[]): number {
|
||||
return (data || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@Post('notify')
|
||||
async sendNotification(): Promise<any> {
|
||||
return this.client.emit<number>('notification', true);
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ConfigurableModuleBuilder } from '@nestjs/common';
|
||||
import { IntegrationModuleOptions } from './interfaces/integration-module-options.interface';
|
||||
|
||||
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
|
||||
new ConfigurableModuleBuilder<IntegrationModuleOptions>()
|
||||
.setClassMethodName('forRoot')
|
||||
.setFactoryMethodName('construct')
|
||||
.setExtras(
|
||||
{
|
||||
isGlobal: true,
|
||||
},
|
||||
(definition, extras) => ({
|
||||
...definition,
|
||||
global: extras.isGlobal,
|
||||
}),
|
||||
)
|
||||
.build();
|
||||
16
integration/module-utils/src/integration.module.ts
Normal file
16
integration/module-utils/src/integration.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Inject, Module } from '@nestjs/common';
|
||||
import {
|
||||
ConfigurableModuleClass,
|
||||
MODULE_OPTIONS_TOKEN,
|
||||
} from './integration.module-definition';
|
||||
import { IntegrationModuleOptions } from './interfaces/integration-module-options.interface';
|
||||
|
||||
@Module({})
|
||||
export class IntegrationModule extends ConfigurableModuleClass {
|
||||
constructor(
|
||||
@Inject(MODULE_OPTIONS_TOKEN)
|
||||
public readonly options: IntegrationModuleOptions,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface IntegrationModuleOptions {
|
||||
url: string;
|
||||
secure?: boolean;
|
||||
}
|
||||
47
integration/module-utils/test/integration-module.spec.ts
Normal file
47
integration/module-utils/test/integration-module.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { IntegrationModule } from '../src/integration.module';
|
||||
|
||||
describe('Module utils (ConfigurableModuleBuilder)', () => {
|
||||
it('should auto-generate "forRoot" method', async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [
|
||||
IntegrationModule.forRoot({
|
||||
isGlobal: true,
|
||||
url: 'test_url',
|
||||
secure: false,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const integrationModule = moduleRef.get(IntegrationModule);
|
||||
|
||||
expect(integrationModule.options).to.deep.equal({
|
||||
url: 'test_url',
|
||||
secure: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should auto-generate "forRootAsync" method', async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [
|
||||
IntegrationModule.forRootAsync({
|
||||
isGlobal: true,
|
||||
useFactory: () => {
|
||||
return {
|
||||
url: 'test_url',
|
||||
secure: false,
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const integrationModule = moduleRef.get(IntegrationModule);
|
||||
|
||||
expect(integrationModule.options).to.deep.equal({
|
||||
url: 'test_url',
|
||||
secure: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
22
integration/module-utils/tsconfig.json
Normal file
22
integration/module-utils/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"e2e/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
]
|
||||
}
|
||||
906
integration/mosquitto.conf
Normal file
906
integration/mosquitto.conf
Normal file
@@ -0,0 +1,906 @@
|
||||
# Config file for mosquitto
|
||||
#
|
||||
# See mosquitto.conf(5) for more information.
|
||||
#
|
||||
# Default values are shown, uncomment to change.
|
||||
#
|
||||
# Use the # character to indicate a comment, but only if it is the
|
||||
# very first character on the line.
|
||||
|
||||
# =================================================================
|
||||
# General configuration
|
||||
# =================================================================
|
||||
|
||||
# Use per listener security settings.
|
||||
#
|
||||
# It is recommended this option be set before any other options.
|
||||
#
|
||||
# If this option is set to true, then all authentication and access control
|
||||
# options are controlled on a per listener basis. The following options are
|
||||
# affected:
|
||||
#
|
||||
# acl_file
|
||||
# allow_anonymous
|
||||
# allow_zero_length_clientid
|
||||
# auto_id_prefix
|
||||
# password_file
|
||||
# plugin
|
||||
# plugin_opt_*
|
||||
# psk_file
|
||||
#
|
||||
# Note that if set to true, then a durable client (i.e. with clean session set
|
||||
# to false) that has disconnected will use the ACL settings defined for the
|
||||
# listener that it was most recently connected to.
|
||||
#
|
||||
# The default behaviour is for this to be set to false, which maintains the
|
||||
# setting behaviour from previous versions of mosquitto.
|
||||
#per_listener_settings false
|
||||
|
||||
|
||||
# This option controls whether a client is allowed to connect with a zero
|
||||
# length client id or not. This option only affects clients using MQTT v3.1.1
|
||||
# and later. If set to false, clients connecting with a zero length client id
|
||||
# are disconnected. If set to true, clients will be allocated a client id by
|
||||
# the broker. This means it is only useful for clients with clean session set
|
||||
# to true.
|
||||
#allow_zero_length_clientid true
|
||||
|
||||
# If allow_zero_length_clientid is true, this option allows you to set a prefix
|
||||
# to automatically generated client ids to aid visibility in logs.
|
||||
# Defaults to 'auto-'
|
||||
#auto_id_prefix auto-
|
||||
|
||||
# This option affects the scenario when a client subscribes to a topic that has
|
||||
# retained messages. It is possible that the client that published the retained
|
||||
# message to the topic had access at the time they published, but that access
|
||||
# has been subsequently removed. If check_retain_source is set to true, the
|
||||
# default, the source of a retained message will be checked for access rights
|
||||
# before it is republished. When set to false, no check will be made and the
|
||||
# retained message will always be published. This affects all listeners.
|
||||
#check_retain_source true
|
||||
|
||||
# QoS 1 and 2 messages will be allowed inflight per client until this limit
|
||||
# is exceeded. Defaults to 0. (No maximum)
|
||||
# See also max_inflight_messages
|
||||
#max_inflight_bytes 0
|
||||
|
||||
# The maximum number of QoS 1 and 2 messages currently inflight per
|
||||
# client.
|
||||
# This includes messages that are partway through handshakes and
|
||||
# those that are being retried. Defaults to 20. Set to 0 for no
|
||||
# maximum. Setting to 1 will guarantee in-order delivery of QoS 1
|
||||
# and 2 messages.
|
||||
#max_inflight_messages 20
|
||||
|
||||
# For MQTT v5 clients, it is possible to have the server send a "server
|
||||
# keepalive" value that will override the keepalive value set by the client.
|
||||
# This is intended to be used as a mechanism to say that the server will
|
||||
# disconnect the client earlier than it anticipated, and that the client should
|
||||
# use the new keepalive value. The max_keepalive option allows you to specify
|
||||
# that clients may only connect with keepalive less than or equal to this
|
||||
# value, otherwise they will be sent a server keepalive telling them to use
|
||||
# max_keepalive. This only applies to MQTT v5 clients. The default, and maximum
|
||||
# value allowable, is 65535.
|
||||
#
|
||||
# Set to 0 to allow clients to set keepalive = 0, which means no keepalive
|
||||
# checks are made and the client will never be disconnected by the broker if no
|
||||
# messages are received. You should be very sure this is the behaviour that you
|
||||
# want.
|
||||
#
|
||||
# For MQTT v3.1.1 and v3.1 clients, there is no mechanism to tell the client
|
||||
# what keepalive value they should use. If an MQTT v3.1.1 or v3.1 client
|
||||
# specifies a keepalive time greater than max_keepalive they will be sent a
|
||||
# CONNACK message with the "identifier rejected" reason code, and disconnected.
|
||||
#
|
||||
#max_keepalive 65535
|
||||
|
||||
# For MQTT v5 clients, it is possible to have the server send a "maximum packet
|
||||
# size" value that will instruct the client it will not accept MQTT packets
|
||||
# with size greater than max_packet_size bytes. This applies to the full MQTT
|
||||
# packet, not just the payload. Setting this option to a positive value will
|
||||
# set the maximum packet size to that number of bytes. If a client sends a
|
||||
# packet which is larger than this value, it will be disconnected. This applies
|
||||
# to all clients regardless of the protocol version they are using, but v3.1.1
|
||||
# and earlier clients will of course not have received the maximum packet size
|
||||
# information. Defaults to no limit. Setting below 20 bytes is forbidden
|
||||
# because it is likely to interfere with ordinary client operation, even with
|
||||
# very small payloads.
|
||||
#max_packet_size 0
|
||||
|
||||
# QoS 1 and 2 messages above those currently in-flight will be queued per
|
||||
# client until this limit is exceeded. Defaults to 0. (No maximum)
|
||||
# See also max_queued_messages.
|
||||
# If both max_queued_messages and max_queued_bytes are specified, packets will
|
||||
# be queued until the first limit is reached.
|
||||
#max_queued_bytes 0
|
||||
|
||||
# Set the maximum QoS supported. Clients publishing at a QoS higher than
|
||||
# specified here will be disconnected.
|
||||
#max_qos 2
|
||||
|
||||
# The maximum number of QoS 1 and 2 messages to hold in a queue per client
|
||||
# above those that are currently in-flight. Defaults to 1000. Set
|
||||
# to 0 for no maximum (not recommended).
|
||||
# See also queue_qos0_messages.
|
||||
# See also max_queued_bytes.
|
||||
#max_queued_messages 1000
|
||||
#
|
||||
# This option sets the maximum number of heap memory bytes that the broker will
|
||||
# allocate, and hence sets a hard limit on memory use by the broker. Memory
|
||||
# requests that exceed this value will be denied. The effect will vary
|
||||
# depending on what has been denied. If an incoming message is being processed,
|
||||
# then the message will be dropped and the publishing client will be
|
||||
# disconnected. If an outgoing message is being sent, then the individual
|
||||
# message will be dropped and the receiving client will be disconnected.
|
||||
# Defaults to no limit.
|
||||
#memory_limit 0
|
||||
|
||||
# This option sets the maximum publish payload size that the broker will allow.
|
||||
# Received messages that exceed this size will not be accepted by the broker.
|
||||
# The default value is 0, which means that all valid MQTT messages are
|
||||
# accepted. MQTT imposes a maximum payload size of 268435455 bytes.
|
||||
#message_size_limit 0
|
||||
|
||||
# This option allows persistent clients (those with clean session set to false)
|
||||
# to be removed if they do not reconnect within a certain time frame.
|
||||
#
|
||||
# This is a non-standard option in MQTT V3.1 but allowed in MQTT v3.1.1.
|
||||
#
|
||||
# Badly designed clients may set clean session to false whilst using a randomly
|
||||
# generated client id. This leads to persistent clients that will never
|
||||
# reconnect. This option allows these clients to be removed.
|
||||
#
|
||||
# The expiration period should be an integer followed by one of h d w m y for
|
||||
# hour, day, week, month and year respectively. For example
|
||||
#
|
||||
# persistent_client_expiration 2m
|
||||
# persistent_client_expiration 14d
|
||||
# persistent_client_expiration 1y
|
||||
#
|
||||
# The default if not set is to never expire persistent clients.
|
||||
#persistent_client_expiration
|
||||
|
||||
# Write process id to a file. Default is a blank string which means
|
||||
# a pid file shouldn't be written.
|
||||
# This should be set to /var/run/mosquitto/mosquitto.pid if mosquitto is
|
||||
# being run automatically on boot with an init script and
|
||||
# start-stop-daemon or similar.
|
||||
#pid_file
|
||||
|
||||
# Set to true to queue messages with QoS 0 when a persistent client is
|
||||
# disconnected. These messages are included in the limit imposed by
|
||||
# max_queued_messages and max_queued_bytes
|
||||
# Defaults to false.
|
||||
# This is a non-standard option for the MQTT v3.1 spec but is allowed in
|
||||
# v3.1.1.
|
||||
#queue_qos0_messages false
|
||||
|
||||
# Set to false to disable retained message support. If a client publishes a
|
||||
# message with the retain bit set, it will be disconnected if this is set to
|
||||
# false.
|
||||
#retain_available true
|
||||
|
||||
# Disable Nagle's algorithm on client sockets. This has the effect of reducing
|
||||
# latency of individual messages at the potential cost of increasing the number
|
||||
# of packets being sent.
|
||||
#set_tcp_nodelay false
|
||||
|
||||
# Time in seconds between updates of the $SYS tree.
|
||||
# Set to 0 to disable the publishing of the $SYS tree.
|
||||
#sys_interval 10
|
||||
|
||||
# The MQTT specification requires that the QoS of a message delivered to a
|
||||
# subscriber is never upgraded to match the QoS of the subscription. Enabling
|
||||
# this option changes this behaviour. If upgrade_outgoing_qos is set true,
|
||||
# messages sent to a subscriber will always match the QoS of its subscription.
|
||||
# This is a non-standard option explicitly disallowed by the spec.
|
||||
#upgrade_outgoing_qos false
|
||||
|
||||
# When run as root, drop privileges to this user and its primary
|
||||
# group.
|
||||
# Set to root to stay as root, but this is not recommended.
|
||||
# If set to "mosquitto", or left unset, and the "mosquitto" user does not exist
|
||||
# then it will drop privileges to the "nobody" user instead.
|
||||
# If run as a non-root user, this setting has no effect.
|
||||
# Note that on Windows this has no effect and so mosquitto should be started by
|
||||
# the user you wish it to run as.
|
||||
#user mosquitto
|
||||
|
||||
# =================================================================
|
||||
# Listeners
|
||||
# =================================================================
|
||||
|
||||
# Listen on a port/ip address combination. By using this variable
|
||||
# multiple times, mosquitto can listen on more than one port. If
|
||||
# this variable is used and neither bind_address nor port given,
|
||||
# then the default listener will not be started.
|
||||
# The port number to listen on must be given. Optionally, an ip
|
||||
# address or host name may be supplied as a second argument. In
|
||||
# this case, mosquitto will attempt to bind the listener to that
|
||||
# address and so restrict access to the associated network and
|
||||
# interface. By default, mosquitto will listen on all interfaces.
|
||||
# Note that for a websockets listener it is not possible to bind to a host
|
||||
# name.
|
||||
#
|
||||
# On systems that support Unix Domain Sockets, it is also possible
|
||||
# to create a # Unix socket rather than opening a TCP socket. In
|
||||
# this case, the port number should be set to 0 and a unix socket
|
||||
# path must be provided, e.g.
|
||||
# listener 0 /tmp/mosquitto.sock
|
||||
#
|
||||
# listener port-number [ip address/host name/unix socket path]
|
||||
#listener
|
||||
|
||||
# By default, a listener will attempt to listen on all supported IP protocol
|
||||
# versions. If you do not have an IPv4 or IPv6 interface you may wish to
|
||||
# disable support for either of those protocol versions. In particular, note
|
||||
# that due to the limitations of the websockets library, it will only ever
|
||||
# attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail
|
||||
# if IPv6 is not available.
|
||||
#
|
||||
# Set to `ipv4` to force the listener to only use IPv4, or set to `ipv6` to
|
||||
# force the listener to only use IPv6. If you want support for both IPv4 and
|
||||
# IPv6, then do not use the socket_domain option.
|
||||
#
|
||||
#socket_domain
|
||||
|
||||
# Bind the listener to a specific interface. This is similar to
|
||||
# the [ip address/host name] part of the listener definition, but is useful
|
||||
# when an interface has multiple addresses or the address may change. If used
|
||||
# with the [ip address/host name] part of the listener definition, then the
|
||||
# bind_interface option will take priority.
|
||||
# Not available on Windows.
|
||||
#
|
||||
# Example: bind_interface eth0
|
||||
#bind_interface
|
||||
|
||||
# When a listener is using the websockets protocol, it is possible to serve
|
||||
# http data as well. Set http_dir to a directory which contains the files you
|
||||
# wish to serve. If this option is not specified, then no normal http
|
||||
# connections will be possible.
|
||||
#http_dir
|
||||
|
||||
# The maximum number of client connections to allow. This is
|
||||
# a per listener setting.
|
||||
# Default is -1, which means unlimited connections.
|
||||
# Note that other process limits mean that unlimited connections
|
||||
# are not really possible. Typically the default maximum number of
|
||||
# connections possible is around 1024.
|
||||
#max_connections -1
|
||||
|
||||
# The listener can be restricted to operating within a topic hierarchy using
|
||||
# the mount_point option. This is achieved be prefixing the mount_point string
|
||||
# to all topics for any clients connected to this listener. This prefixing only
|
||||
# happens internally to the broker; the client will not see the prefix.
|
||||
#mount_point
|
||||
|
||||
# Choose the protocol to use when listening.
|
||||
# This can be either mqtt or websockets.
|
||||
# Certificate based TLS may be used with websockets, except that only the
|
||||
# cafile, certfile, keyfile, ciphers, and ciphers_tls13 options are supported.
|
||||
#protocol mqtt
|
||||
listener 1883
|
||||
protocol mqtt
|
||||
|
||||
listener 9001
|
||||
protocol websockets
|
||||
|
||||
# Set use_username_as_clientid to true to replace the clientid that a client
|
||||
# connected with with its username. This allows authentication to be tied to
|
||||
# the clientid, which means that it is possible to prevent one client
|
||||
# disconnecting another by using the same clientid.
|
||||
# If a client connects with no username it will be disconnected as not
|
||||
# authorised when this option is set to true.
|
||||
# Do not use in conjunction with clientid_prefixes.
|
||||
# See also use_identity_as_username.
|
||||
#use_username_as_clientid
|
||||
|
||||
# Change the websockets headers size. This is a global option, it is not
|
||||
# possible to set per listener. This option sets the size of the buffer used in
|
||||
# the libwebsockets library when reading HTTP headers. If you are passing large
|
||||
# header data such as cookies then you may need to increase this value. If left
|
||||
# unset, or set to 0, then the default of 1024 bytes will be used.
|
||||
#websockets_headers_size
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Certificate based SSL/TLS support
|
||||
# -----------------------------------------------------------------
|
||||
# The following options can be used to enable certificate based SSL/TLS support
|
||||
# for this listener. Note that the recommended port for MQTT over TLS is 8883,
|
||||
# but this must be set manually.
|
||||
#
|
||||
# See also the mosquitto-tls man page and the "Pre-shared-key based SSL/TLS
|
||||
# support" section. Only one of certificate or PSK encryption support can be
|
||||
# enabled for any listener.
|
||||
|
||||
# Both of certfile and keyfile must be defined to enable certificate based
|
||||
# TLS encryption.
|
||||
|
||||
# Path to the PEM encoded server certificate.
|
||||
#certfile
|
||||
|
||||
# Path to the PEM encoded keyfile.
|
||||
#keyfile
|
||||
|
||||
# If you wish to control which encryption ciphers are used, use the ciphers
|
||||
# option. The list of available ciphers can be optained using the "openssl
|
||||
# ciphers" command and should be provided in the same format as the output of
|
||||
# that command. This applies to TLS 1.2 and earlier versions only. Use
|
||||
# ciphers_tls1.3 for TLS v1.3.
|
||||
#ciphers
|
||||
|
||||
# Choose which TLS v1.3 ciphersuites are used for this listener.
|
||||
# Defaults to "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
|
||||
#ciphers_tls1.3
|
||||
|
||||
# If you have require_certificate set to true, you can create a certificate
|
||||
# revocation list file to revoke access to particular client certificates. If
|
||||
# you have done this, use crlfile to point to the PEM encoded revocation file.
|
||||
#crlfile
|
||||
|
||||
# To allow the use of ephemeral DH key exchange, which provides forward
|
||||
# security, the listener must load DH parameters. This can be specified with
|
||||
# the dhparamfile option. The dhparamfile can be generated with the command
|
||||
# e.g. "openssl dhparam -out dhparam.pem 2048"
|
||||
#dhparamfile
|
||||
|
||||
# By default an TLS enabled listener will operate in a similar fashion to a
|
||||
# https enabled web server, in that the server has a certificate signed by a CA
|
||||
# and the client will verify that it is a trusted certificate. The overall aim
|
||||
# is encryption of the network traffic. By setting require_certificate to true,
|
||||
# the client must provide a valid certificate in order for the network
|
||||
# connection to proceed. This allows access to the broker to be controlled
|
||||
# outside of the mechanisms provided by MQTT.
|
||||
#require_certificate false
|
||||
|
||||
# cafile and capath define methods of accessing the PEM encoded
|
||||
# Certificate Authority certificates that will be considered trusted when
|
||||
# checking incoming client certificates.
|
||||
# cafile defines the path to a file containing the CA certificates.
|
||||
# capath defines a directory that will be searched for files
|
||||
# containing the CA certificates. For capath to work correctly, the
|
||||
# certificate files must have ".crt" as the file ending and you must run
|
||||
# "openssl rehash <path to capath>" each time you add/remove a certificate.
|
||||
#cafile
|
||||
#capath
|
||||
|
||||
|
||||
# If require_certificate is true, you may set use_identity_as_username to true
|
||||
# to use the CN value from the client certificate as a username. If this is
|
||||
# true, the password_file option will not be used for this listener.
|
||||
#use_identity_as_username false
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Pre-shared-key based SSL/TLS support
|
||||
# -----------------------------------------------------------------
|
||||
# The following options can be used to enable PSK based SSL/TLS support for
|
||||
# this listener. Note that the recommended port for MQTT over TLS is 8883, but
|
||||
# this must be set manually.
|
||||
#
|
||||
# See also the mosquitto-tls man page and the "Certificate based SSL/TLS
|
||||
# support" section. Only one of certificate or PSK encryption support can be
|
||||
# enabled for any listener.
|
||||
|
||||
# The psk_hint option enables pre-shared-key support for this listener and also
|
||||
# acts as an identifier for this listener. The hint is sent to clients and may
|
||||
# be used locally to aid authentication. The hint is a free form string that
|
||||
# doesn't have much meaning in itself, so feel free to be creative.
|
||||
# If this option is provided, see psk_file to define the pre-shared keys to be
|
||||
# used or create a security plugin to handle them.
|
||||
#psk_hint
|
||||
|
||||
# When using PSK, the encryption ciphers used will be chosen from the list of
|
||||
# available PSK ciphers. If you want to control which ciphers are available,
|
||||
# use the "ciphers" option. The list of available ciphers can be optained
|
||||
# using the "openssl ciphers" command and should be provided in the same format
|
||||
# as the output of that command.
|
||||
#ciphers
|
||||
|
||||
# Set use_identity_as_username to have the psk identity sent by the client used
|
||||
# as its username. Authentication will be carried out using the PSK rather than
|
||||
# the MQTT username/password and so password_file will not be used for this
|
||||
# listener.
|
||||
#use_identity_as_username false
|
||||
|
||||
|
||||
# =================================================================
|
||||
# Persistence
|
||||
# =================================================================
|
||||
|
||||
# If persistence is enabled, save the in-memory database to disk
|
||||
# every autosave_interval seconds. If set to 0, the persistence
|
||||
# database will only be written when mosquitto exits. See also
|
||||
# autosave_on_changes.
|
||||
# Note that writing of the persistence database can be forced by
|
||||
# sending mosquitto a SIGUSR1 signal.
|
||||
#autosave_interval 1800
|
||||
|
||||
# If true, mosquitto will count the number of subscription changes, retained
|
||||
# messages received and queued messages and if the total exceeds
|
||||
# autosave_interval then the in-memory database will be saved to disk.
|
||||
# If false, mosquitto will save the in-memory database to disk by treating
|
||||
# autosave_interval as a time in seconds.
|
||||
#autosave_on_changes false
|
||||
|
||||
# Save persistent message data to disk (true/false).
|
||||
# This saves information about all messages, including
|
||||
# subscriptions, currently in-flight messages and retained
|
||||
# messages.
|
||||
# retained_persistence is a synonym for this option.
|
||||
#persistence false
|
||||
|
||||
# The filename to use for the persistent database, not including
|
||||
# the path.
|
||||
#persistence_file mosquitto.db
|
||||
|
||||
# Location for persistent database.
|
||||
# Default is an empty string (current directory).
|
||||
# Set to e.g. /var/lib/mosquitto if running as a proper service on Linux or
|
||||
# similar.
|
||||
#persistence_location
|
||||
|
||||
|
||||
# =================================================================
|
||||
# Logging
|
||||
# =================================================================
|
||||
|
||||
# Places to log to. Use multiple log_dest lines for multiple
|
||||
# logging destinations.
|
||||
# Possible destinations are: stdout stderr syslog topic file dlt
|
||||
#
|
||||
# stdout and stderr log to the console on the named output.
|
||||
#
|
||||
# syslog uses the userspace syslog facility which usually ends up
|
||||
# in /var/log/messages or similar.
|
||||
#
|
||||
# topic logs to the broker topic '$SYS/broker/log/<severity>',
|
||||
# where severity is one of D, E, W, N, I, M which are debug, error,
|
||||
# warning, notice, information and message. Message type severity is used by
|
||||
# the subscribe/unsubscribe log_types and publishes log messages to
|
||||
# $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe.
|
||||
#
|
||||
# The file destination requires an additional parameter which is the file to be
|
||||
# logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be
|
||||
# closed and reopened when the broker receives a HUP signal. Only a single file
|
||||
# destination may be configured.
|
||||
#
|
||||
# The dlt destination is for the automotive `Diagnostic Log and Trace` tool.
|
||||
# This requires that Mosquitto has been compiled with DLT support.
|
||||
#
|
||||
# Note that if the broker is running as a Windows service it will default to
|
||||
# "log_dest none" and neither stdout nor stderr logging is available.
|
||||
# Use "log_dest none" if you wish to disable logging.
|
||||
#log_dest stderr
|
||||
|
||||
# Types of messages to log. Use multiple log_type lines for logging
|
||||
# multiple types of messages.
|
||||
# Possible types are: debug, error, warning, notice, information,
|
||||
# none, subscribe, unsubscribe, websockets, all.
|
||||
# Note that debug type messages are for decoding the incoming/outgoing
|
||||
# network packets. They are not logged in "topics".
|
||||
#log_type error
|
||||
#log_type warning
|
||||
#log_type notice
|
||||
#log_type information
|
||||
|
||||
|
||||
# If set to true, client connection and disconnection messages will be included
|
||||
# in the log.
|
||||
#connection_messages true
|
||||
|
||||
# If using syslog logging (not on Windows), messages will be logged to the
|
||||
# "daemon" facility by default. Use the log_facility option to choose which of
|
||||
# local0 to local7 to log to instead. The option value should be an integer
|
||||
# value, e.g. "log_facility 5" to use local5.
|
||||
#log_facility
|
||||
|
||||
# If set to true, add a timestamp value to each log message.
|
||||
#log_timestamp true
|
||||
|
||||
# Set the format of the log timestamp. If left unset, this is the number of
|
||||
# seconds since the Unix epoch.
|
||||
# This is a free text string which will be passed to the strftime function. To
|
||||
# get an ISO 8601 datetime, for example:
|
||||
# log_timestamp_format %Y-%m-%dT%H:%M:%S
|
||||
#log_timestamp_format
|
||||
|
||||
# Change the websockets logging level. This is a global option, it is not
|
||||
# possible to set per listener. This is an integer that is interpreted by
|
||||
# libwebsockets as a bit mask for its lws_log_levels enum. See the
|
||||
# libwebsockets documentation for more details. "log_type websockets" must also
|
||||
# be enabled.
|
||||
#websockets_log_level 0
|
||||
|
||||
|
||||
# =================================================================
|
||||
# Security
|
||||
# =================================================================
|
||||
|
||||
# If set, only clients that have a matching prefix on their
|
||||
# clientid will be allowed to connect to the broker. By default,
|
||||
# all clients may connect.
|
||||
# For example, setting "secure-" here would mean a client "secure-
|
||||
# client" could connect but another with clientid "mqtt" couldn't.
|
||||
#clientid_prefixes
|
||||
|
||||
# Boolean value that determines whether clients that connect
|
||||
# without providing a username are allowed to connect. If set to
|
||||
# false then a password file should be created (see the
|
||||
# password_file option) to control authenticated client access.
|
||||
#
|
||||
# Defaults to false, unless there are no listeners defined in the configuration
|
||||
# file, in which case it is set to true, but connections are only allowed from
|
||||
# the local machine.
|
||||
allow_anonymous true
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Default authentication and topic access control
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# Control access to the broker using a password file. This file can be
|
||||
# generated using the mosquitto_passwd utility. If TLS support is not compiled
|
||||
# into mosquitto (it is recommended that TLS support should be included) then
|
||||
# plain text passwords are used, in which case the file should be a text file
|
||||
# with lines in the format:
|
||||
# username:password
|
||||
# The password (and colon) may be omitted if desired, although this
|
||||
# offers very little in the way of security.
|
||||
#
|
||||
# See the TLS client require_certificate and use_identity_as_username options
|
||||
# for alternative authentication options. If a plugin is used as well as
|
||||
# password_file, the plugin check will be made first.
|
||||
#password_file
|
||||
|
||||
# Access may also be controlled using a pre-shared-key file. This requires
|
||||
# TLS-PSK support and a listener configured to use it. The file should be text
|
||||
# lines in the format:
|
||||
# identity:key
|
||||
# The key should be in hexadecimal format without a leading "0x".
|
||||
# If an plugin is used as well, the plugin check will be made first.
|
||||
#psk_file
|
||||
|
||||
# Control access to topics on the broker using an access control list
|
||||
# file. If this parameter is defined then only the topics listed will
|
||||
# have access.
|
||||
# If the first character of a line of the ACL file is a # it is treated as a
|
||||
# comment.
|
||||
# Topic access is added with lines of the format:
|
||||
#
|
||||
# topic [read|write|readwrite|deny] <topic>
|
||||
#
|
||||
# The access type is controlled using "read", "write", "readwrite" or "deny".
|
||||
# This parameter is optional (unless <topic> contains a space character) - if
|
||||
# not given then the access is read/write. <topic> can contain the + or #
|
||||
# wildcards as in subscriptions.
|
||||
#
|
||||
# The "deny" option can used to explicity deny access to a topic that would
|
||||
# otherwise be granted by a broader read/write/readwrite statement. Any "deny"
|
||||
# topics are handled before topics that grant read/write access.
|
||||
#
|
||||
# The first set of topics are applied to anonymous clients, assuming
|
||||
# allow_anonymous is true. User specific topic ACLs are added after a
|
||||
# user line as follows:
|
||||
#
|
||||
# user <username>
|
||||
#
|
||||
# The username referred to here is the same as in password_file. It is
|
||||
# not the clientid.
|
||||
#
|
||||
#
|
||||
# If is also possible to define ACLs based on pattern substitution within the
|
||||
# topic. The patterns available for substition are:
|
||||
#
|
||||
# %c to match the client id of the client
|
||||
# %u to match the username of the client
|
||||
#
|
||||
# The substitution pattern must be the only text for that level of hierarchy.
|
||||
#
|
||||
# The form is the same as for the topic keyword, but using pattern as the
|
||||
# keyword.
|
||||
# Pattern ACLs apply to all users even if the "user" keyword has previously
|
||||
# been given.
|
||||
#
|
||||
# If using bridges with usernames and ACLs, connection messages can be allowed
|
||||
# with the following pattern:
|
||||
# pattern write $SYS/broker/connection/%c/state
|
||||
#
|
||||
# pattern [read|write|readwrite] <topic>
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# pattern write sensor/%u/data
|
||||
#
|
||||
# If an plugin is used as well as acl_file, the plugin check will be
|
||||
# made first.
|
||||
#acl_file
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# External authentication and topic access plugin options
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# External authentication and access control can be supported with the
|
||||
# plugin option. This is a path to a loadable plugin. See also the
|
||||
# plugin_opt_* options described below.
|
||||
#
|
||||
# The plugin option can be specified multiple times to load multiple
|
||||
# plugins. The plugins will be processed in the order that they are specified
|
||||
# here. If the plugin option is specified alongside either of
|
||||
# password_file or acl_file then the plugin checks will be made first.
|
||||
#
|
||||
# If the per_listener_settings option is false, the plugin will be apply to all
|
||||
# listeners. If per_listener_settings is true, then the plugin will apply to
|
||||
# the current listener being defined only.
|
||||
#
|
||||
# This option is also available as `auth_plugin`, but this use is deprecated
|
||||
# and will be removed in the future.
|
||||
#
|
||||
#plugin
|
||||
|
||||
# If the plugin option above is used, define options to pass to the
|
||||
# plugin here as described by the plugin instructions. All options named
|
||||
# using the format plugin_opt_* will be passed to the plugin, for example:
|
||||
#
|
||||
# This option is also available as `auth_opt_*`, but this use is deprecated
|
||||
# and will be removed in the future.
|
||||
#
|
||||
# plugin_opt_db_host
|
||||
# plugin_opt_db_port
|
||||
# plugin_opt_db_username
|
||||
# plugin_opt_db_password
|
||||
|
||||
|
||||
# =================================================================
|
||||
# Bridges
|
||||
# =================================================================
|
||||
|
||||
# A bridge is a way of connecting multiple MQTT brokers together.
|
||||
# Create a new bridge using the "connection" option as described below. Set
|
||||
# options for the bridges using the remaining parameters. You must specify the
|
||||
# address and at least one topic to subscribe to.
|
||||
#
|
||||
# Each connection must have a unique name.
|
||||
#
|
||||
# The address line may have multiple host address and ports specified. See
|
||||
# below in the round_robin description for more details on bridge behaviour if
|
||||
# multiple addresses are used. Note that if you use an IPv6 address, then you
|
||||
# are required to specify a port.
|
||||
#
|
||||
# The direction that the topic will be shared can be chosen by
|
||||
# specifying out, in or both, where the default value is out.
|
||||
# The QoS level of the bridged communication can be specified with the next
|
||||
# topic option. The default QoS level is 0, to change the QoS the topic
|
||||
# direction must also be given.
|
||||
#
|
||||
# The local and remote prefix options allow a topic to be remapped when it is
|
||||
# bridged to/from the remote broker. This provides the ability to place a topic
|
||||
# tree in an appropriate location.
|
||||
#
|
||||
# For more details see the mosquitto.conf man page.
|
||||
#
|
||||
# Multiple topics can be specified per connection, but be careful
|
||||
# not to create any loops.
|
||||
#
|
||||
# If you are using bridges with cleansession set to false (the default), then
|
||||
# you may get unexpected behaviour from incoming topics if you change what
|
||||
# topics you are subscribing to. This is because the remote broker keeps the
|
||||
# subscription for the old topic. If you have this problem, connect your bridge
|
||||
# with cleansession set to true, then reconnect with cleansession set to false
|
||||
# as normal.
|
||||
#connection <name>
|
||||
#address <host>[:<port>] [<host>[:<port>]]
|
||||
#topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix]
|
||||
|
||||
# If you need to have the bridge connect over a particular network interface,
|
||||
# use bridge_bind_address to tell the bridge which local IP address the socket
|
||||
# should bind to, e.g. `bridge_bind_address 192.168.1.10`
|
||||
#bridge_bind_address
|
||||
|
||||
# If a bridge has topics that have "out" direction, the default behaviour is to
|
||||
# send an unsubscribe request to the remote broker on that topic. This means
|
||||
# that changing a topic direction from "in" to "out" will not keep receiving
|
||||
# incoming messages. Sending these unsubscribe requests is not always
|
||||
# desirable, setting bridge_attempt_unsubscribe to false will disable sending
|
||||
# the unsubscribe request.
|
||||
#bridge_attempt_unsubscribe true
|
||||
|
||||
# Set the version of the MQTT protocol to use with for this bridge. Can be one
|
||||
# of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311.
|
||||
#bridge_protocol_version mqttv311
|
||||
|
||||
# Set the clean session variable for this bridge.
|
||||
# When set to true, when the bridge disconnects for any reason, all
|
||||
# messages and subscriptions will be cleaned up on the remote
|
||||
# broker. Note that with cleansession set to true, there may be a
|
||||
# significant amount of retained messages sent when the bridge
|
||||
# reconnects after losing its connection.
|
||||
# When set to false, the subscriptions and messages are kept on the
|
||||
# remote broker, and delivered when the bridge reconnects.
|
||||
#cleansession false
|
||||
|
||||
# Set the amount of time a bridge using the lazy start type must be idle before
|
||||
# it will be stopped. Defaults to 60 seconds.
|
||||
#idle_timeout 60
|
||||
|
||||
# Set the keepalive interval for this bridge connection, in
|
||||
# seconds.
|
||||
#keepalive_interval 60
|
||||
|
||||
# Set the clientid to use on the local broker. If not defined, this defaults to
|
||||
# 'local.<clientid>'. If you are bridging a broker to itself, it is important
|
||||
# that local_clientid and clientid do not match.
|
||||
#local_clientid
|
||||
|
||||
# If set to true, publish notification messages to the local and remote brokers
|
||||
# giving information about the state of the bridge connection. Retained
|
||||
# messages are published to the topic $SYS/broker/connection/<clientid>/state
|
||||
# unless the notification_topic option is used.
|
||||
# If the message is 1 then the connection is active, or 0 if the connection has
|
||||
# failed.
|
||||
# This uses the last will and testament feature.
|
||||
#notifications true
|
||||
|
||||
# Choose the topic on which notification messages for this bridge are
|
||||
# published. If not set, messages are published on the topic
|
||||
# $SYS/broker/connection/<clientid>/state
|
||||
#notification_topic
|
||||
|
||||
# Set the client id to use on the remote end of this bridge connection. If not
|
||||
# defined, this defaults to 'name.hostname' where name is the connection name
|
||||
# and hostname is the hostname of this computer.
|
||||
# This replaces the old "clientid" option to avoid confusion. "clientid"
|
||||
# remains valid for the time being.
|
||||
#remote_clientid
|
||||
|
||||
# Set the password to use when connecting to a broker that requires
|
||||
# authentication. This option is only used if remote_username is also set.
|
||||
# This replaces the old "password" option to avoid confusion. "password"
|
||||
# remains valid for the time being.
|
||||
#remote_password
|
||||
|
||||
# Set the username to use when connecting to a broker that requires
|
||||
# authentication.
|
||||
# This replaces the old "username" option to avoid confusion. "username"
|
||||
# remains valid for the time being.
|
||||
#remote_username
|
||||
|
||||
# Set the amount of time a bridge using the automatic start type will wait
|
||||
# until attempting to reconnect.
|
||||
# This option can be configured to use a constant delay time in seconds, or to
|
||||
# use a backoff mechanism based on "Decorrelated Jitter", which adds a degree
|
||||
# of randomness to when the restart occurs.
|
||||
#
|
||||
# Set a constant timeout of 20 seconds:
|
||||
# restart_timeout 20
|
||||
#
|
||||
# Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of
|
||||
# 60 seconds:
|
||||
# restart_timeout 10 30
|
||||
#
|
||||
# Defaults to jitter with a base of 5 and cap of 30
|
||||
#restart_timeout 5 30
|
||||
|
||||
# If the bridge has more than one address given in the address/addresses
|
||||
# configuration, the round_robin option defines the behaviour of the bridge on
|
||||
# a failure of the bridge connection. If round_robin is false, the default
|
||||
# value, then the first address is treated as the main bridge connection. If
|
||||
# the connection fails, the other secondary addresses will be attempted in
|
||||
# turn. Whilst connected to a secondary bridge, the bridge will periodically
|
||||
# attempt to reconnect to the main bridge until successful.
|
||||
# If round_robin is true, then all addresses are treated as equals. If a
|
||||
# connection fails, the next address will be tried and if successful will
|
||||
# remain connected until it fails
|
||||
#round_robin false
|
||||
|
||||
# Set the start type of the bridge. This controls how the bridge starts and
|
||||
# can be one of three types: automatic, lazy and once. Note that RSMB provides
|
||||
# a fourth start type "manual" which isn't currently supported by mosquitto.
|
||||
#
|
||||
# "automatic" is the default start type and means that the bridge connection
|
||||
# will be started automatically when the broker starts and also restarted
|
||||
# after a short delay (30 seconds) if the connection fails.
|
||||
#
|
||||
# Bridges using the "lazy" start type will be started automatically when the
|
||||
# number of queued messages exceeds the number set with the "threshold"
|
||||
# parameter. It will be stopped automatically after the time set by the
|
||||
# "idle_timeout" parameter. Use this start type if you wish the connection to
|
||||
# only be active when it is needed.
|
||||
#
|
||||
# A bridge using the "once" start type will be started automatically when the
|
||||
# broker starts but will not be restarted if the connection fails.
|
||||
#start_type automatic
|
||||
|
||||
# Set the number of messages that need to be queued for a bridge with lazy
|
||||
# start type to be restarted. Defaults to 10 messages.
|
||||
# Must be less than max_queued_messages.
|
||||
#threshold 10
|
||||
|
||||
# If try_private is set to true, the bridge will attempt to indicate to the
|
||||
# remote broker that it is a bridge not an ordinary client. If successful, this
|
||||
# means that loop detection will be more effective and that retained messages
|
||||
# will be propagated correctly. Not all brokers support this feature so it may
|
||||
# be necessary to set try_private to false if your bridge does not connect
|
||||
# properly.
|
||||
#try_private true
|
||||
|
||||
# Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism
|
||||
# for brokers to tell clients that they do not support retained messages, but
|
||||
# this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a
|
||||
# v3.1.1 or v3.1 broker that does not support retained messages, set the
|
||||
# bridge_outgoing_retain option to false. This will remove the retain bit on
|
||||
# all outgoing messages to that bridge, regardless of any other setting.
|
||||
#bridge_outgoing_retain true
|
||||
|
||||
# If you wish to restrict the size of messages sent to a remote bridge, use the
|
||||
# bridge_max_packet_size option. This sets the maximum number of bytes for
|
||||
# the total message, including headers and payload.
|
||||
# Note that MQTT v5 brokers may provide their own maximum-packet-size property.
|
||||
# In this case, the smaller of the two limits will be used.
|
||||
# Set to 0 for "unlimited".
|
||||
#bridge_max_packet_size 0
|
||||
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Certificate based SSL/TLS support
|
||||
# -----------------------------------------------------------------
|
||||
# Either bridge_cafile or bridge_capath must be defined to enable TLS support
|
||||
# for this bridge.
|
||||
# bridge_cafile defines the path to a file containing the
|
||||
# Certificate Authority certificates that have signed the remote broker
|
||||
# certificate.
|
||||
# bridge_capath defines a directory that will be searched for files containing
|
||||
# the CA certificates. For bridge_capath to work correctly, the certificate
|
||||
# files must have ".crt" as the file ending and you must run "openssl rehash
|
||||
# <path to capath>" each time you add/remove a certificate.
|
||||
#bridge_cafile
|
||||
#bridge_capath
|
||||
|
||||
|
||||
# If the remote broker has more than one protocol available on its port, e.g.
|
||||
# MQTT and WebSockets, then use bridge_alpn to configure which protocol is
|
||||
# requested. Note that WebSockets support for bridges is not yet available.
|
||||
#bridge_alpn
|
||||
|
||||
# When using certificate based encryption, bridge_insecure disables
|
||||
# verification of the server hostname in the server certificate. This can be
|
||||
# useful when testing initial server configurations, but makes it possible for
|
||||
# a malicious third party to impersonate your server through DNS spoofing, for
|
||||
# example. Use this option in testing only. If you need to resort to using this
|
||||
# option in a production environment, your setup is at fault and there is no
|
||||
# point using encryption.
|
||||
#bridge_insecure false
|
||||
|
||||
# Path to the PEM encoded client certificate, if required by the remote broker.
|
||||
#bridge_certfile
|
||||
|
||||
# Path to the PEM encoded client private key, if required by the remote broker.
|
||||
#bridge_keyfile
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# PSK based SSL/TLS support
|
||||
# -----------------------------------------------------------------
|
||||
# Pre-shared-key encryption provides an alternative to certificate based
|
||||
# encryption. A bridge can be configured to use PSK with the bridge_identity
|
||||
# and bridge_psk options. These are the client PSK identity, and pre-shared-key
|
||||
# in hexadecimal format with no "0x". Only one of certificate and PSK based
|
||||
# encryption can be used on one
|
||||
# bridge at once.
|
||||
#bridge_identity
|
||||
#bridge_psk
|
||||
|
||||
|
||||
# =================================================================
|
||||
# External config files
|
||||
# =================================================================
|
||||
|
||||
# External configuration files may be included by using the
|
||||
# include_dir option. This defines a directory that will be searched
|
||||
# for config files. All files that end in '.conf' will be loaded as
|
||||
# a configuration file. It is best to have this as the last option
|
||||
# in the main file. This option will only be processed from the main
|
||||
# configuration file. The directory specified must not contain the
|
||||
# main configuration file.
|
||||
# Files within include_dir will be loaded sorted in case-sensitive
|
||||
# alphabetical order, with capital letters ordered first. If this option is
|
||||
# given multiple times, all of the files from the first instance will be
|
||||
# processed before the next instance. See the man page for examples.
|
||||
#include_dir
|
||||
@@ -108,15 +108,15 @@ describe('Global prefix', () => {
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
|
||||
await request(server).get('/hello/foo').expect(200);
|
||||
await request(server)
|
||||
.get('/hello/foo')
|
||||
.expect(200, 'Hello: Data attached in middleware');
|
||||
|
||||
await request(server)
|
||||
.get('/middleware/foo')
|
||||
.expect(200, MIDDLEWARE_PARAM_VALUE);
|
||||
|
||||
await request(server)
|
||||
.get('/api/v1/middleware/foo')
|
||||
.expect(404);
|
||||
await request(server).get('/api/v1/middleware/foo').expect(404);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Controller, Get, Post } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Req } from '@nestjs/common';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
@Get('hello/:name')
|
||||
getHello(): string {
|
||||
return 'hello';
|
||||
getHello(@Req() req): string {
|
||||
return 'Hello: ' + req.extras?.data;
|
||||
}
|
||||
|
||||
@Get('health')
|
||||
|
||||
@@ -17,6 +17,11 @@ export class AppModule {
|
||||
.apply((req, res, next) => res.end(MIDDLEWARE_PARAM_VALUE))
|
||||
.forRoutes({ path: MIDDLEWARE_VALUE + '/*', method: RequestMethod.GET })
|
||||
.apply((req, res, next) => res.status(201).end(MIDDLEWARE_PARAM_VALUE))
|
||||
.forRoutes({ path: MIDDLEWARE_VALUE + '/*', method: RequestMethod.POST });
|
||||
.forRoutes({ path: MIDDLEWARE_VALUE + '/*', method: RequestMethod.POST })
|
||||
.apply((req, res, next) => {
|
||||
req.extras = { data: 'Data attached in middleware' };
|
||||
next();
|
||||
})
|
||||
.forRoutes({ path: '*', method: RequestMethod.GET });
|
||||
}
|
||||
}
|
||||
|
||||
49
integration/nest-application/raw-body/e2e/express.spec.ts
Normal file
49
integration/nest-application/raw-body/e2e/express.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { ExpressModule } from '../src/express.module';
|
||||
|
||||
describe('Raw body (Express Application)', () => {
|
||||
let app: NestExpressApplication;
|
||||
const body = '{ "amount":0.0 }';
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture = await Test.createTestingModule({
|
||||
imports: [ExpressModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication<NestExpressApplication>(
|
||||
undefined,
|
||||
{ rawBody: true },
|
||||
);
|
||||
});
|
||||
|
||||
it('should return exact post body', async () => {
|
||||
await app.init();
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send(body)
|
||||
.expect(201);
|
||||
|
||||
expect(response.body).to.eql({
|
||||
parsed: {
|
||||
amount: 0,
|
||||
},
|
||||
raw: '{ "amount":0.0 }',
|
||||
});
|
||||
});
|
||||
|
||||
it('should work if post body is empty', async () => {
|
||||
await app.init();
|
||||
await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(201);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
49
integration/nest-application/raw-body/e2e/fastify.spec.ts
Normal file
49
integration/nest-application/raw-body/e2e/fastify.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { FastifyModule } from '../src/fastify.module';
|
||||
|
||||
describe('Raw body (Fastify Application)', () => {
|
||||
let app: NestFastifyApplication;
|
||||
const body = '{ "amount":0.0 }';
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture = await Test.createTestingModule({
|
||||
imports: [FastifyModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication<NestFastifyApplication>(null, {
|
||||
rawBody: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return exact post body', async () => {
|
||||
await app.init();
|
||||
const response = await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('Accept', 'application/json')
|
||||
.send(body)
|
||||
.expect(201);
|
||||
|
||||
expect(response.body).to.eql({
|
||||
parsed: {
|
||||
amount: 0,
|
||||
},
|
||||
raw: '{ "amount":0.0 }',
|
||||
});
|
||||
});
|
||||
|
||||
it('should work if post body is empty', async () => {
|
||||
await app.init();
|
||||
await request(app.getHttpServer())
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(201);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Controller, Post, RawBodyRequest, Req } from '@nestjs/common';
|
||||
import { Request } from 'express';
|
||||
|
||||
@Controller()
|
||||
export class ExpressController {
|
||||
@Post()
|
||||
getRawBody(@Req() req: RawBodyRequest<Request>) {
|
||||
return {
|
||||
parsed: req.body,
|
||||
raw: req.rawBody.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ExpressController } from './express.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ExpressController],
|
||||
})
|
||||
export class ExpressModule {}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Controller, Post, RawBodyRequest, Req } from '@nestjs/common';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
|
||||
@Controller()
|
||||
export class FastifyController {
|
||||
@Post()
|
||||
getRawBody(@Req() req: RawBodyRequest<FastifyRequest>) {
|
||||
return {
|
||||
parsed: req.body,
|
||||
raw: req.rawBody.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { FastifyController } from './fastify.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [FastifyController],
|
||||
})
|
||||
export class FastifyModule {}
|
||||
22
integration/nest-application/raw-body/tsconfig.json
Normal file
22
integration/nest-application/raw-body/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"e2e/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -14,7 +14,7 @@ async function createNestApp(...gateways): Promise<INestApplication> {
|
||||
const testingModule = await Test.createTestingModule({
|
||||
providers: gateways,
|
||||
}).compile();
|
||||
const app = await testingModule.createNestApplication();
|
||||
const app = testingModule.createNestApplication();
|
||||
app.useWebSocketAdapter(new WsAdapter(app) as any);
|
||||
return app;
|
||||
}
|
||||
@@ -40,6 +40,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
await new Promise<void>(resolve =>
|
||||
ws.on('message', data => {
|
||||
expect(JSON.parse(data).data.test).to.be.eql('test');
|
||||
ws.close();
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
@@ -63,6 +64,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
await new Promise<void>(resolve =>
|
||||
ws.on('message', data => {
|
||||
expect(JSON.parse(data).data.test).to.be.eql('test');
|
||||
ws.close();
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
@@ -89,6 +91,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
await new Promise<void>(resolve =>
|
||||
ws.on('message', data => {
|
||||
expect(JSON.parse(data).data.test).to.be.eql('test');
|
||||
ws.close();
|
||||
resolve();
|
||||
}),
|
||||
);
|
||||
@@ -113,6 +116,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
ws.on('open', () => {
|
||||
ws.on('message', data => {
|
||||
expect(JSON.parse(data).data.test).to.be.eql('test');
|
||||
ws.close();
|
||||
resolve();
|
||||
});
|
||||
ws.send(
|
||||
@@ -129,6 +133,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
await new Promise<void>(resolve => {
|
||||
ws2.on('message', data => {
|
||||
expect(JSON.parse(data).data.test).to.be.eql('test');
|
||||
ws2.close();
|
||||
resolve();
|
||||
});
|
||||
ws2.send(
|
||||
@@ -158,6 +163,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
ws.on('open', () => {
|
||||
ws.on('message', data => {
|
||||
expect(JSON.parse(data).data.test).to.be.eql('test');
|
||||
ws.close();
|
||||
resolve();
|
||||
});
|
||||
ws.send(
|
||||
@@ -174,6 +180,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
await new Promise<void>(resolve => {
|
||||
ws2.on('message', data => {
|
||||
expect(JSON.parse(data).data.test).to.be.eql('test');
|
||||
ws2.close();
|
||||
resolve();
|
||||
});
|
||||
ws2.send(
|
||||
@@ -187,5 +194,7 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => app.close());
|
||||
afterEach(async function () {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "8.3.1"
|
||||
"version": "8.4.5"
|
||||
}
|
||||
|
||||
7202
package-lock.json
generated
7202
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
139
package.json
139
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/core",
|
||||
"version": "8.3.1",
|
||||
"version": "8.4.4",
|
||||
"description": "Modern, fast, powerful node.js web framework",
|
||||
"homepage": "https://nestjs.com",
|
||||
"repository": {
|
||||
@@ -25,8 +25,8 @@
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"format": "prettier \"**/*.ts\" --ignore-path ./.prettierignore --write && git status",
|
||||
"postinstall": "opencollective",
|
||||
"test": "nyc --require ts-node/register mocha packages/**/*.spec.ts --reporter spec --require 'node_modules/reflect-metadata/Reflect.js' --exit",
|
||||
"test:integration": "mocha \"integration/*/{,!(node_modules)/**/}/*.spec.ts\" --reporter spec --require ts-node/register --require 'node_modules/reflect-metadata/Reflect.js' --exit",
|
||||
"test": "nyc mocha packages/**/*.spec.ts --reporter spec",
|
||||
"test:integration": "mocha \"integration/*/{,!(node_modules)/**/}/*.spec.ts\" --reporter spec",
|
||||
"test:docker:up": "docker-compose -f integration/docker-compose.yml up -d",
|
||||
"test:docker:down": "docker-compose -f integration/docker-compose.yml down",
|
||||
"lint": "concurrently 'npm run lint:packages' 'npm run lint:integration' 'npm run lint:spec'",
|
||||
@@ -34,8 +34,9 @@
|
||||
"lint:integration": "eslint 'integration/*/{,!(node_modules)/**/}/*.ts' -c '.eslintrc.spec.js'",
|
||||
"lint:packages": "eslint 'packages/**/**.ts' --ignore-pattern 'packages/**/*.spec.ts'",
|
||||
"lint:spec": "eslint 'packages/**/**.spec.ts' -c '.eslintrc.spec.js'",
|
||||
"prerelease": "gulp copy-misc && gulp build --dist node_modules/@nestjs && npm run changelog | pbcopy",
|
||||
"prerelease": "gulp copy-misc && gulp build --dist node_modules/@nestjs",
|
||||
"publish": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --force-publish --access public --exact -m \"chore(@nestjs) publish %s release\"",
|
||||
"prepublishOnly": "npm run changelog | pbcopy",
|
||||
"publish:beta": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=beta --access public -m \"chore(@nestjs) publish %s release\"",
|
||||
"publish:next": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=next --access public --skip-git -m \"chore(@nestjs) publish %s release\"",
|
||||
"publish:rc": "npm run prerelease && npm run build:prod && ./node_modules/.bin/lerna publish --npm-tag=rc --access public -m \"chore(@nestjs) publish %s release\"",
|
||||
@@ -49,83 +50,83 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/opencollective": "0.3.2",
|
||||
"axios": "0.26.0",
|
||||
"axios": "0.27.2",
|
||||
"class-transformer": "0.5.1",
|
||||
"class-validator": "0.13.2",
|
||||
"cli-color": "2.0.1",
|
||||
"cli-color": "2.0.2",
|
||||
"cors": "2.8.5",
|
||||
"express": "4.17.3",
|
||||
"fast-json-stringify": "3.0.3",
|
||||
"express": "4.18.1",
|
||||
"fast-json-stringify": "3.2.0",
|
||||
"fast-safe-stringify": "2.1.1",
|
||||
"graphql-subscriptions": "2.0.0",
|
||||
"iterare": "1.2.1",
|
||||
"object-hash": "3.0.0",
|
||||
"path-to-regexp": "3.2.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "7.5.4",
|
||||
"socket.io": "4.4.1",
|
||||
"tslib": "2.3.1",
|
||||
"rxjs": "7.5.5",
|
||||
"socket.io": "4.5.0",
|
||||
"tslib": "2.4.0",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codechecks/client": "0.1.12",
|
||||
"@commitlint/cli": "16.2.1",
|
||||
"@commitlint/config-angular": "16.2.1",
|
||||
"@grpc/grpc-js": "1.5.7",
|
||||
"@grpc/proto-loader": "0.6.9",
|
||||
"@nestjs/apollo": "10.0.5",
|
||||
"@nestjs/graphql": "10.0.5",
|
||||
"@nestjs/mongoose": "9.0.2",
|
||||
"@commitlint/cli": "17.0.0",
|
||||
"@commitlint/config-angular": "17.0.0",
|
||||
"@fastify/cors": "7.0.0",
|
||||
"@fastify/formbody": "6.0.0",
|
||||
"@fastify/multipart": "6.0.0",
|
||||
"@fastify/static": "5.0.0",
|
||||
"@grpc/grpc-js": "1.6.7",
|
||||
"@grpc/proto-loader": "0.6.12",
|
||||
"@nestjs/apollo": "10.0.11",
|
||||
"@nestjs/graphql": "10.0.8",
|
||||
"@nestjs/mongoose": "9.0.3",
|
||||
"@nestjs/typeorm": "8.0.3",
|
||||
"@types/amqplib": "0.8.2",
|
||||
"@types/bytes": "3.1.1",
|
||||
"@types/cache-manager": "3.4.3",
|
||||
"@types/chai": "4.3.0",
|
||||
"@types/chai": "4.3.1",
|
||||
"@types/chai-as-promised": "7.1.5",
|
||||
"@types/cors": "2.8.12",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/gulp": "4.0.9",
|
||||
"@types/http-errors": "1.8.2",
|
||||
"@types/mocha": "9.1.0",
|
||||
"@types/node": "17.0.21",
|
||||
"@types/redis": "4.0.11",
|
||||
"@types/mocha": "9.1.1",
|
||||
"@types/node": "17.0.34",
|
||||
"@types/reflect-metadata": "0.1.0",
|
||||
"@types/sinon": "10.0.11",
|
||||
"@types/socket.io": "3.0.2",
|
||||
"@types/ws": "8.5.1",
|
||||
"@types/supertest": "2.0.12",
|
||||
"@types/ws": "8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "4.33.0",
|
||||
"@typescript-eslint/parser": "4.33.0",
|
||||
"amqp-connection-manager": "4.1.1",
|
||||
"amqplib": "0.8.0",
|
||||
"apollo-server-core": "3.6.3",
|
||||
"apollo-server-express": "3.6.3",
|
||||
"amqp-connection-manager": "4.1.3",
|
||||
"amqplib": "0.9.0",
|
||||
"apollo-server-core": "3.7.0",
|
||||
"apollo-server-express": "3.7.0",
|
||||
"artillery": "1.7.9",
|
||||
"body-parser": "1.19.2",
|
||||
"body-parser": "1.20.0",
|
||||
"bytes": "3.1.2",
|
||||
"cache-manager": "3.6.0",
|
||||
"cache-manager": "3.6.1",
|
||||
"cache-manager-redis-store": "2.0.0",
|
||||
"chai": "4.3.6",
|
||||
"chai-as-promised": "7.1.1",
|
||||
"clang-format": "1.6.0",
|
||||
"clang-format": "1.8.0",
|
||||
"commitlint-circle": "1.0.0",
|
||||
"concurrently": "7.0.0",
|
||||
"concurrently": "7.2.0",
|
||||
"conventional-changelog": "3.1.25",
|
||||
"core-js": "3.21.1",
|
||||
"core-js": "3.22.5",
|
||||
"coveralls": "3.1.1",
|
||||
"delete-empty": "3.0.0",
|
||||
"engine.io-client": "6.1.1",
|
||||
"engine.io-client": "6.2.2",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-prettier": "8.4.0",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eventsource": "1.1.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eventsource": "2.0.2",
|
||||
"fancy-log": "2.0.0",
|
||||
"fastify": "3.27.2",
|
||||
"fastify-cors": "6.0.3",
|
||||
"fastify-formbody": "5.2.0",
|
||||
"fastify-multipart": "5.3.1",
|
||||
"fastify-static": "4.5.0",
|
||||
"graphql": "15.7.2",
|
||||
"graphql-tools": "7.0.5",
|
||||
"fastify": "3.29.0",
|
||||
"graphql": "15.8.0",
|
||||
"graphql-tools": "8.2.9",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-clang-format": "1.0.27",
|
||||
"gulp-clean": "0.4.0",
|
||||
@@ -133,43 +134,43 @@
|
||||
"gulp-typescript": "5.0.1",
|
||||
"gulp-watch": "5.0.1",
|
||||
"http-errors": "2.0.0",
|
||||
"husky": "7.0.4",
|
||||
"husky": "8.0.1",
|
||||
"imports-loader": "3.1.1",
|
||||
"ioredis": "5.0.4",
|
||||
"json-loader": "0.5.7",
|
||||
"kafkajs": "1.16.0",
|
||||
"lerna": "2.11.0",
|
||||
"kafkajs": "2.0.0",
|
||||
"lerna": "3.0.0",
|
||||
"lerna-changelog": "2.2.0",
|
||||
"light-my-request": "4.8.0",
|
||||
"lint-staged": "12.3.4",
|
||||
"light-my-request": "4.10.1",
|
||||
"lint-staged": "12.4.1",
|
||||
"markdown-table": "2.0.0",
|
||||
"merge-graphql-schemas": "1.7.8",
|
||||
"middie": "6.0.0",
|
||||
"mocha": "9.2.1",
|
||||
"mongoose": "6.2.4",
|
||||
"mqtt": "4.3.6",
|
||||
"middie": "6.1.0",
|
||||
"mocha": "10.0.0",
|
||||
"mongoose": "6.3.3",
|
||||
"mqtt": "4.3.7",
|
||||
"multer": "1.4.4",
|
||||
"mysql2": "2.3.3",
|
||||
"nats": "2.6.1",
|
||||
"nodemon": "2.0.15",
|
||||
"nodemon": "2.0.16",
|
||||
"nyc": "15.1.0",
|
||||
"point-of-view": "5.1.0",
|
||||
"prettier": "2.5.1",
|
||||
"redis": "3.1.2",
|
||||
"point-of-view": "5.3.0",
|
||||
"prettier": "2.6.2",
|
||||
"rxjs-compat": "6.6.7",
|
||||
"sinon": "13.0.1",
|
||||
"sinon": "14.0.0",
|
||||
"sinon-chai": "3.7.0",
|
||||
"socket.io-client": "4.4.1",
|
||||
"socket.io-client": "4.5.0",
|
||||
"subscriptions-transport-ws": "0.11.0",
|
||||
"supertest": "6.2.2",
|
||||
"ts-morph": "13.0.3",
|
||||
"ts-node": "10.5.0",
|
||||
"typeorm": "0.2.44",
|
||||
"typescript": "4.3.5",
|
||||
"supertest": "6.2.3",
|
||||
"ts-morph": "14.0.0",
|
||||
"ts-node": "10.7.0",
|
||||
"typeorm": "0.2.45",
|
||||
"typescript": "4.6.4",
|
||||
"wrk": "1.2.1",
|
||||
"ws": "7.5.6"
|
||||
"ws": "8.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
"node": ">= 12.9.0"
|
||||
},
|
||||
"collective": {
|
||||
"type": "opencollective",
|
||||
@@ -223,5 +224,13 @@
|
||||
],
|
||||
"sourceMap": true,
|
||||
"instrument": true
|
||||
},
|
||||
"mocha": {
|
||||
"require": [
|
||||
"ts-node/register",
|
||||
"node_modules/reflect-metadata/Reflect.js",
|
||||
"hooks/mocha-init-hook.ts"
|
||||
],
|
||||
"exit": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support from the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsors
|
||||
<table style="text-align:center;"><tr>
|
||||
@@ -64,17 +64,20 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://careers.labster.com/departments/platform" target="_blank"><img src="https://nestjs.com/img/labster-logo.png" width="170" valign="middle" /></a></td><td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="150" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></</tr></table>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="110" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td></</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
|
||||
1
packages/common/cache/cache.constants.ts
vendored
1
packages/common/cache/cache.constants.ts
vendored
@@ -1,4 +1,3 @@
|
||||
export const CACHE_MANAGER = 'CACHE_MANAGER';
|
||||
export const CACHE_MODULE_OPTIONS = 'CACHE_MODULE_OPTIONS';
|
||||
export const CACHE_KEY_METADATA = 'cache_module:cache_key';
|
||||
export const CACHE_TTL_METADATA = 'cache_module:cache_ttl';
|
||||
|
||||
12
packages/common/cache/cache.module-definition.ts
vendored
Normal file
12
packages/common/cache/cache.module-definition.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ConfigurableModuleBuilder } from '../module-utils';
|
||||
import {
|
||||
CacheModuleOptions,
|
||||
CacheOptionsFactory,
|
||||
} from './interfaces/cache-module.interface';
|
||||
|
||||
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
|
||||
new ConfigurableModuleBuilder<CacheModuleOptions>({
|
||||
moduleName: 'Cache',
|
||||
})
|
||||
.setFactoryMethodName('createCacheOptions' as keyof CacheOptionsFactory)
|
||||
.build();
|
||||
53
packages/common/cache/cache.module.ts
vendored
53
packages/common/cache/cache.module.ts
vendored
@@ -1,11 +1,11 @@
|
||||
import { Module } from '../decorators';
|
||||
import { DynamicModule, Provider } from '../interfaces';
|
||||
import { CACHE_MANAGER, CACHE_MODULE_OPTIONS } from './cache.constants';
|
||||
import { DynamicModule } from '../interfaces';
|
||||
import { CACHE_MANAGER } from './cache.constants';
|
||||
import { ConfigurableModuleClass } from './cache.module-definition';
|
||||
import { createCacheManager } from './cache.providers';
|
||||
import {
|
||||
CacheModuleAsyncOptions,
|
||||
CacheModuleOptions,
|
||||
CacheOptionsFactory,
|
||||
} from './interfaces/cache-module.interface';
|
||||
|
||||
/**
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
providers: [createCacheManager()],
|
||||
exports: [CACHE_MANAGER],
|
||||
})
|
||||
export class CacheModule {
|
||||
export class CacheModule extends ConfigurableModuleClass {
|
||||
/**
|
||||
* Configure the cache manager statically.
|
||||
*
|
||||
@@ -31,9 +31,8 @@ export class CacheModule {
|
||||
options: CacheModuleOptions<StoreConfig> = {} as any,
|
||||
): DynamicModule {
|
||||
return {
|
||||
module: CacheModule,
|
||||
global: options.isGlobal,
|
||||
providers: [{ provide: CACHE_MODULE_OPTIONS, useValue: options }],
|
||||
...super.register(options),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,47 +47,11 @@ export class CacheModule {
|
||||
static registerAsync<
|
||||
StoreConfig extends Record<any, any> = Record<string, any>,
|
||||
>(options: CacheModuleAsyncOptions<StoreConfig>): DynamicModule {
|
||||
const moduleDefinition = super.registerAsync(options);
|
||||
return {
|
||||
module: CacheModule,
|
||||
global: options.isGlobal,
|
||||
imports: options.imports,
|
||||
providers: [
|
||||
...this.createAsyncProviders<StoreConfig>(options),
|
||||
...(options.extraProviders || []),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private static createAsyncProviders<StoreConfig extends Record<any, any>>(
|
||||
options: CacheModuleAsyncOptions<StoreConfig>,
|
||||
): Provider[] {
|
||||
if (options.useExisting || options.useFactory) {
|
||||
return [this.createAsyncOptionsProvider(options)];
|
||||
}
|
||||
return [
|
||||
this.createAsyncOptionsProvider(options),
|
||||
{
|
||||
provide: options.useClass,
|
||||
useClass: options.useClass,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private static createAsyncOptionsProvider<
|
||||
StoreConfig extends Record<any, any>,
|
||||
>(options: CacheModuleAsyncOptions<StoreConfig>): Provider {
|
||||
if (options.useFactory) {
|
||||
return {
|
||||
provide: CACHE_MODULE_OPTIONS,
|
||||
useFactory: options.useFactory,
|
||||
inject: options.inject || [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
provide: CACHE_MODULE_OPTIONS,
|
||||
useFactory: async (optionsFactory: CacheOptionsFactory<StoreConfig>) =>
|
||||
optionsFactory.createCacheOptions(),
|
||||
inject: [options.useExisting || options.useClass],
|
||||
...moduleDefinition,
|
||||
providers: moduleDefinition.providers.concat(options.extraProviders),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
5
packages/common/cache/cache.providers.ts
vendored
5
packages/common/cache/cache.providers.ts
vendored
@@ -1,6 +1,7 @@
|
||||
import { Provider } from '../interfaces';
|
||||
import { loadPackage } from '../utils/load-package.util';
|
||||
import { CACHE_MANAGER, CACHE_MODULE_OPTIONS } from './cache.constants';
|
||||
import { CACHE_MANAGER } from './cache.constants';
|
||||
import { MODULE_OPTIONS_TOKEN } from './cache.module-definition';
|
||||
import { defaultCacheOptions } from './default-options';
|
||||
import { CacheManagerOptions } from './interfaces/cache-manager.interface';
|
||||
|
||||
@@ -31,6 +32,6 @@ export function createCacheManager(): Provider {
|
||||
...(options || {}),
|
||||
});
|
||||
},
|
||||
inject: [CACHE_MODULE_OPTIONS],
|
||||
inject: [MODULE_OPTIONS_TOKEN],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ModuleMetadata, Provider, Type } from '../../interfaces';
|
||||
import { Provider, Type } from '../../interfaces';
|
||||
import { ConfigurableModuleAsyncOptions } from '../../module-utils';
|
||||
import { CacheManagerOptions } from './cache-manager.interface';
|
||||
|
||||
export type CacheModuleOptions<
|
||||
@@ -39,7 +40,10 @@ export interface CacheOptionsFactory<
|
||||
*/
|
||||
export interface CacheModuleAsyncOptions<
|
||||
StoreConfig extends Record<any, any> = Record<string, any>,
|
||||
> extends Pick<ModuleMetadata, 'imports'> {
|
||||
> extends ConfigurableModuleAsyncOptions<
|
||||
CacheModuleOptions<StoreConfig>,
|
||||
keyof CacheOptionsFactory
|
||||
> {
|
||||
/**
|
||||
* Injection token resolving to an existing provider. The provider must implement
|
||||
* the `CacheOptionsFactory` interface.
|
||||
@@ -63,6 +67,9 @@ export interface CacheModuleAsyncOptions<
|
||||
* Dependencies that a Factory may inject.
|
||||
*/
|
||||
inject?: any[];
|
||||
/**
|
||||
* Extra providers to be registered within a scope of this module.
|
||||
*/
|
||||
extraProviders?: Provider[];
|
||||
/**
|
||||
* If "true', register `CacheModule` as a global module.
|
||||
|
||||
@@ -7,7 +7,7 @@ import { RENDER_METADATA } from '../../constants';
|
||||
*
|
||||
* @param template name of the render engine template file
|
||||
*
|
||||
* @see [Model-View-Controller](https://docs.nestjs.com/techniques.mvc)
|
||||
* @see [Model-View-Controller](https://docs.nestjs.com/techniques/mvc)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Bad Gateway* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Bad Request* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Conflict* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Forbidden* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Gateway Timeout* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Gone* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Http Version Not Supported* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { isObject, isString } from '../utils/shared.utils';
|
||||
* Defines the base Nest HTTP exception, which is handled by the default
|
||||
* Exceptions Handler.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -7,7 +7,7 @@ import { HttpException } from './http.exception';
|
||||
* Any attempt to brew coffee with a teapot should result in the error code
|
||||
* "418 I'm a teapot". The resulting entity body MAY be short and stout.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Internal Server Error* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Method Not Allowed* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Misdirected* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Not Acceptable* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Not Found* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Not Implemented* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Payload Too Large* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Precondition Failed* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Request Timeout* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Service Unavailable* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Unauthorized* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Unprocessable Entity* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -4,7 +4,7 @@ import { HttpException } from './http.exception';
|
||||
/**
|
||||
* Defines an HTTP exception for *Unsupported Media Type* type errors.
|
||||
*
|
||||
* @see [Base Exceptions](https://docs.nestjs.com/exception-filters#base-exceptions)
|
||||
* @see [Built-in HTTP exceptions](https://docs.nestjs.com/exception-filters#built-in-http-exceptions)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,7 @@ export class StreamableFile {
|
||||
this.stream = new Readable();
|
||||
this.stream.push(bufferOrReadStream);
|
||||
this.stream.push(null);
|
||||
this.options.length ??= bufferOrReadStream.length;
|
||||
} else if (bufferOrReadStream.pipe && isFunction(bufferOrReadStream.pipe)) {
|
||||
this.stream = bufferOrReadStream;
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
export const AXIOS_INSTANCE_TOKEN = 'AXIOS_INSTANCE_TOKEN';
|
||||
export const HTTP_MODULE_ID = 'HTTP_MODULE_ID';
|
||||
export const HTTP_MODULE_OPTIONS = 'HTTP_MODULE_OPTIONS';
|
||||
@@ -1,99 +0,0 @@
|
||||
import Axios from 'axios';
|
||||
import { Module } from '../decorators/modules/module.decorator';
|
||||
import { DynamicModule, Provider } from '../interfaces';
|
||||
import { randomStringGenerator } from '../utils/random-string-generator.util';
|
||||
import {
|
||||
AXIOS_INSTANCE_TOKEN,
|
||||
HTTP_MODULE_ID,
|
||||
HTTP_MODULE_OPTIONS,
|
||||
} from './http.constants';
|
||||
import { HttpService } from './http.service';
|
||||
import {
|
||||
HttpModuleAsyncOptions,
|
||||
HttpModuleOptions,
|
||||
HttpModuleOptionsFactory,
|
||||
} from './interfaces';
|
||||
|
||||
/**
|
||||
* @deprecated "HttpModule" (from the "@nestjs/common" package) is deprecated and will be removed in the next major release. Please, use the "@nestjs/axios" package instead.
|
||||
*/
|
||||
@Module({
|
||||
providers: [
|
||||
HttpService,
|
||||
{
|
||||
provide: AXIOS_INSTANCE_TOKEN,
|
||||
useValue: Axios,
|
||||
},
|
||||
],
|
||||
exports: [HttpService],
|
||||
})
|
||||
export class HttpModule {
|
||||
static register(config: HttpModuleOptions): DynamicModule {
|
||||
return {
|
||||
module: HttpModule,
|
||||
providers: [
|
||||
{
|
||||
provide: AXIOS_INSTANCE_TOKEN,
|
||||
useValue: Axios.create(config),
|
||||
},
|
||||
{
|
||||
provide: HTTP_MODULE_ID,
|
||||
useValue: randomStringGenerator(),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
static registerAsync(options: HttpModuleAsyncOptions): DynamicModule {
|
||||
return {
|
||||
module: HttpModule,
|
||||
imports: options.imports,
|
||||
providers: [
|
||||
...this.createAsyncProviders(options),
|
||||
{
|
||||
provide: AXIOS_INSTANCE_TOKEN,
|
||||
useFactory: (config: HttpModuleOptions) => Axios.create(config),
|
||||
inject: [HTTP_MODULE_OPTIONS],
|
||||
},
|
||||
{
|
||||
provide: HTTP_MODULE_ID,
|
||||
useValue: randomStringGenerator(),
|
||||
},
|
||||
...(options.extraProviders || []),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
private static createAsyncProviders(
|
||||
options: HttpModuleAsyncOptions,
|
||||
): Provider[] {
|
||||
if (options.useExisting || options.useFactory) {
|
||||
return [this.createAsyncOptionsProvider(options)];
|
||||
}
|
||||
return [
|
||||
this.createAsyncOptionsProvider(options),
|
||||
{
|
||||
provide: options.useClass,
|
||||
useClass: options.useClass,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private static createAsyncOptionsProvider(
|
||||
options: HttpModuleAsyncOptions,
|
||||
): Provider {
|
||||
if (options.useFactory) {
|
||||
return {
|
||||
provide: HTTP_MODULE_OPTIONS,
|
||||
useFactory: options.useFactory,
|
||||
inject: options.inject || [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
provide: HTTP_MODULE_OPTIONS,
|
||||
useFactory: async (optionsFactory: HttpModuleOptionsFactory) =>
|
||||
optionsFactory.createHttpOptions(),
|
||||
inject: [options.useExisting || options.useClass],
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
import Axios, {
|
||||
AxiosInstance,
|
||||
AxiosPromise,
|
||||
AxiosRequestConfig,
|
||||
AxiosResponse,
|
||||
CancelTokenSource,
|
||||
} from 'axios';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Inject } from '../decorators';
|
||||
import { Logger } from '../services';
|
||||
import { AXIOS_INSTANCE_TOKEN } from './http.constants';
|
||||
|
||||
/**
|
||||
* @deprecated "HttpModule" (from the "@nestjs/common" package) is deprecated and will be removed in the next major release. Please, use the "@nestjs/axios" package instead.
|
||||
*/
|
||||
export class HttpService {
|
||||
private readonly logger = new Logger(HttpService.name);
|
||||
|
||||
constructor(
|
||||
@Inject(AXIOS_INSTANCE_TOKEN)
|
||||
private readonly instance: AxiosInstance = Axios,
|
||||
) {
|
||||
this.logger.warn(
|
||||
'DEPRECATED! "HttpModule" (from the "@nestjs/common" package) is deprecated and will be removed in the next major release. Please, use the "@nestjs/axios" package instead.',
|
||||
);
|
||||
}
|
||||
|
||||
request<T = any>(config: AxiosRequestConfig): Observable<AxiosResponse<T>> {
|
||||
return this.makeObservable<T>(this.instance.request, config);
|
||||
}
|
||||
|
||||
get<T = any>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Observable<AxiosResponse<T>> {
|
||||
return this.makeObservable<T>(this.instance.get, url, config);
|
||||
}
|
||||
|
||||
delete<T = any>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Observable<AxiosResponse<T>> {
|
||||
return this.makeObservable<T>(this.instance.delete, url, config);
|
||||
}
|
||||
|
||||
head<T = any>(
|
||||
url: string,
|
||||
config?: AxiosRequestConfig,
|
||||
): Observable<AxiosResponse<T>> {
|
||||
return this.makeObservable<T>(this.instance.head, url, config);
|
||||
}
|
||||
|
||||
post<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: AxiosRequestConfig,
|
||||
): Observable<AxiosResponse<T>> {
|
||||
return this.makeObservable<T>(this.instance.post, url, data, config);
|
||||
}
|
||||
|
||||
put<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: AxiosRequestConfig,
|
||||
): Observable<AxiosResponse<T>> {
|
||||
return this.makeObservable<T>(this.instance.put, url, data, config);
|
||||
}
|
||||
|
||||
patch<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: AxiosRequestConfig,
|
||||
): Observable<AxiosResponse<T>> {
|
||||
return this.makeObservable<T>(this.instance.patch, url, data, config);
|
||||
}
|
||||
|
||||
get axiosRef(): AxiosInstance {
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
private makeObservable<T>(
|
||||
axios: (...args: any[]) => AxiosPromise<T>,
|
||||
...args: any[]
|
||||
) {
|
||||
return new Observable<AxiosResponse<T>>(subscriber => {
|
||||
const config: AxiosRequestConfig = { ...(args[args.length - 1] || {}) };
|
||||
|
||||
let cancelSource: CancelTokenSource;
|
||||
if (!config.cancelToken) {
|
||||
cancelSource = Axios.CancelToken.source();
|
||||
config.cancelToken = cancelSource.token;
|
||||
}
|
||||
|
||||
axios(...args)
|
||||
.then(res => {
|
||||
subscriber.next(res);
|
||||
subscriber.complete();
|
||||
})
|
||||
.catch(err => {
|
||||
subscriber.error(err);
|
||||
});
|
||||
return () => {
|
||||
if (config.responseType === 'stream') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelSource) {
|
||||
cancelSource.cancel();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './http.module';
|
||||
export * from './http.service';
|
||||
export * from './interfaces';
|
||||
@@ -1,19 +0,0 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { ModuleMetadata, Provider, Type } from '../../interfaces';
|
||||
|
||||
export type HttpModuleOptions = AxiosRequestConfig;
|
||||
|
||||
export interface HttpModuleOptionsFactory {
|
||||
createHttpOptions(): Promise<HttpModuleOptions> | HttpModuleOptions;
|
||||
}
|
||||
|
||||
export interface HttpModuleAsyncOptions
|
||||
extends Pick<ModuleMetadata, 'imports'> {
|
||||
useExisting?: Type<HttpModuleOptionsFactory>;
|
||||
useClass?: Type<HttpModuleOptionsFactory>;
|
||||
useFactory?: (
|
||||
...args: any[]
|
||||
) => Promise<HttpModuleOptions> | HttpModuleOptions;
|
||||
inject?: any[];
|
||||
extraProviders?: Provider[];
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './http-module.interface';
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Nest @common
|
||||
* Copyright(c) 2017 - 2021 Kamil Mysliwiec
|
||||
* Copyright(c) 2017 - 2022 Kamil Mysliwiec
|
||||
* https://nestjs.com
|
||||
* MIT Licensed
|
||||
*/
|
||||
@@ -11,7 +11,6 @@ export * from './decorators';
|
||||
export * from './enums';
|
||||
export * from './exceptions';
|
||||
export * from './file-stream';
|
||||
export * from './http';
|
||||
export {
|
||||
Abstract,
|
||||
ArgumentMetadata,
|
||||
@@ -49,6 +48,7 @@ export {
|
||||
Paramtype,
|
||||
PipeTransform,
|
||||
Provider,
|
||||
RawBodyRequest,
|
||||
RpcExceptionFilter,
|
||||
Scope,
|
||||
ScopeOptions,
|
||||
@@ -61,6 +61,7 @@ export {
|
||||
WsExceptionFilter,
|
||||
WsMessageHandler,
|
||||
} from './interfaces';
|
||||
export * from './module-utils';
|
||||
export * from './pipes';
|
||||
export * from './serializer';
|
||||
export * from './services';
|
||||
|
||||
@@ -67,7 +67,7 @@ export interface HttpServer<TRequest = any, TResponse = any> {
|
||||
getRequestMethod?(request: TRequest): string;
|
||||
getRequestUrl?(request: TRequest): string;
|
||||
getInstance(): any;
|
||||
registerParserMiddleware(): any;
|
||||
registerParserMiddleware(...args: any[]): any;
|
||||
enableCors(options: CorsOptions | CorsOptionsDelegate<TRequest>): any;
|
||||
getHttpServer(): any;
|
||||
initHttpServer(options: NestApplicationOptions): void;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './http-server.interface';
|
||||
export * from './message-event.interface';
|
||||
export * from './raw-body-request.interface';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type RawBodyRequest<T> = T & { rawBody?: Buffer };
|
||||
@@ -28,7 +28,7 @@ export type Provider<T = any> =
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see [Use class](https://docs.nestjs.com/fundamentals/custom-providers#use-class)
|
||||
* @see [Class providers](https://docs.nestjs.com/fundamentals/custom-providers#class-providers-useclass)
|
||||
* @see [Injection scopes](https://docs.nestjs.com/fundamentals/injection-scopes)
|
||||
*
|
||||
* @publicApi
|
||||
@@ -46,6 +46,12 @@ export interface ClassProvider<T = any> {
|
||||
* Optional enum defining lifetime of the provider that is injected.
|
||||
*/
|
||||
scope?: Scope;
|
||||
/**
|
||||
* This option is only available on factory providers!
|
||||
*
|
||||
* @see [Use factory](https://docs.nestjs.com/fundamentals/custom-providers#factory-providers-usefactory)
|
||||
*/
|
||||
inject?: never;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,7 +65,7 @@ export interface ClassProvider<T = any> {
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see [Use value](https://docs.nestjs.com/fundamentals/custom-providers#use-value)
|
||||
* @see [Value providers](https://docs.nestjs.com/fundamentals/custom-providers#value-providers-usevalue)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
@@ -72,6 +78,12 @@ export interface ValueProvider<T = any> {
|
||||
* Instance of a provider to be injected.
|
||||
*/
|
||||
useValue: T;
|
||||
/**
|
||||
* This option is only available on factory providers!
|
||||
*
|
||||
* @see [Use factory](https://docs.nestjs.com/fundamentals/custom-providers#factory-providers-usefactory)
|
||||
*/
|
||||
inject?: never;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +101,7 @@ export interface ValueProvider<T = any> {
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see [Use factory](https://docs.nestjs.com/fundamentals/custom-providers#use-factory)
|
||||
* @see [Factory providers](https://docs.nestjs.com/fundamentals/custom-providers#factory-providers-usefactory)
|
||||
* @see [Injection scopes](https://docs.nestjs.com/fundamentals/injection-scopes)
|
||||
*
|
||||
* @publicApi
|
||||
@@ -102,7 +114,7 @@ export interface FactoryProvider<T = any> {
|
||||
/**
|
||||
* Factory function that returns an instance of the provider to be injected.
|
||||
*/
|
||||
useFactory: (...args: any[]) => T;
|
||||
useFactory: (...args: any[]) => T | Promise<T>;
|
||||
/**
|
||||
* Optional list of providers to be injected into the context of the Factory function.
|
||||
*/
|
||||
@@ -124,7 +136,7 @@ export interface FactoryProvider<T = any> {
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @see [Use existing](https://docs.nestjs.com/fundamentals/custom-providers#use-existing)
|
||||
* @see [Alias providers](https://docs.nestjs.com/fundamentals/custom-providers#alias-providers-useexisting)
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
||||
@@ -21,4 +21,8 @@ export interface NestApplicationOptions extends NestApplicationContextOptions {
|
||||
* Set of configurable HTTPS options
|
||||
*/
|
||||
httpsOptions?: HttpsOptions;
|
||||
/**
|
||||
* Whether to register the raw request body on the request. Use `req.rawBody`.
|
||||
*/
|
||||
rawBody?: boolean;
|
||||
}
|
||||
|
||||
330
packages/common/module-utils/configurable-module.builder.ts
Normal file
330
packages/common/module-utils/configurable-module.builder.ts
Normal file
@@ -0,0 +1,330 @@
|
||||
import { DynamicModule, Provider } from '../interfaces';
|
||||
import { Logger } from '../services/logger.service';
|
||||
import { randomStringGenerator } from '../utils/random-string-generator.util';
|
||||
import {
|
||||
ASYNC_METHOD_SUFFIX,
|
||||
CONFIGURABLE_MODULE_ID,
|
||||
DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
DEFAULT_METHOD_KEY,
|
||||
} from './constants';
|
||||
import {
|
||||
ConfigurableModuleAsyncOptions,
|
||||
ConfigurableModuleCls,
|
||||
ConfigurableModuleOptionsFactory,
|
||||
} from './interfaces';
|
||||
import { ConfigurableModuleHost } from './interfaces/configurable-module-host.interface';
|
||||
import { generateOptionsInjectionToken } from './utils/generate-options-injection-token.util';
|
||||
|
||||
/**
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ConfigurableModuleBuilderOptions {
|
||||
/**
|
||||
* Specified what injection token should be used for the module options provider.
|
||||
* By default, an auto-generated UUID will be used.
|
||||
*/
|
||||
optionsInjectionToken?: string | symbol;
|
||||
/**
|
||||
* By default, an UUID will be used as a module options provider token.
|
||||
* Explicitly specifying the "moduleName" will instruct the "ConfigurableModuleBuilder"
|
||||
* to use a more descriptive provider token.
|
||||
*
|
||||
* For example, if `moduleName: "Cache"` then auto-generated provider token will be "CACHE_MODULE_OPTIONS".
|
||||
*/
|
||||
moduleName?: string;
|
||||
/**
|
||||
* Indicates whether module should always be "transient", meaning,
|
||||
* every time you call the static method to construct a dynamic module,
|
||||
* regardless of what arguments you pass in, a new "unique" module will be created.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
alwaysTransient?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory that lets you create configurable modules and
|
||||
* provides a way to reduce the majority of dynamic module boilerplate.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export class ConfigurableModuleBuilder<
|
||||
ModuleOptions,
|
||||
StaticMethodKey extends string = typeof DEFAULT_METHOD_KEY,
|
||||
FactoryClassMethodKey extends string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
ExtraModuleDefinitionOptions = {},
|
||||
> {
|
||||
protected staticMethodKey: StaticMethodKey;
|
||||
protected factoryClassMethodKey: FactoryClassMethodKey;
|
||||
protected extras: ExtraModuleDefinitionOptions;
|
||||
protected transformModuleDefinition: (
|
||||
definition: DynamicModule,
|
||||
extraOptions: ExtraModuleDefinitionOptions,
|
||||
) => DynamicModule;
|
||||
|
||||
protected readonly logger = new Logger(ConfigurableModuleBuilder.name);
|
||||
|
||||
constructor(
|
||||
protected readonly options: ConfigurableModuleBuilderOptions = {},
|
||||
parentBuilder?: ConfigurableModuleBuilder<ModuleOptions>,
|
||||
) {
|
||||
if (parentBuilder) {
|
||||
this.staticMethodKey = parentBuilder.staticMethodKey as StaticMethodKey;
|
||||
this.factoryClassMethodKey =
|
||||
parentBuilder.factoryClassMethodKey as FactoryClassMethodKey;
|
||||
this.transformModuleDefinition = parentBuilder.transformModuleDefinition;
|
||||
this.extras = parentBuilder.extras as ExtraModuleDefinitionOptions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the "extras" object (a set of extra options that can be used to modify the dynamic module definition).
|
||||
* Values you specify within the "extras" object will be used as default values (that can be overriden by module consumers).
|
||||
*
|
||||
* This method also applies the so-called "module definition transform function" that takes the auto-generated
|
||||
* dynamic module object ("DynamicModule") and the actual consumer "extras" object as input parameters.
|
||||
* The "extras" object consists of values explicitly specified by module consumers and default values.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* .setExtras<{ isGlobal?: boolean }>({ isGlobal: false }, (definition, extras) =>
|
||||
* ({ ...definition, global: extras.isGlobal })
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
setExtras<ExtraModuleDefinitionOptions>(
|
||||
extras: ExtraModuleDefinitionOptions,
|
||||
transformDefinition: (
|
||||
definition: DynamicModule,
|
||||
extras: ExtraModuleDefinitionOptions,
|
||||
) => DynamicModule,
|
||||
) {
|
||||
const builder = new ConfigurableModuleBuilder<
|
||||
ModuleOptions,
|
||||
StaticMethodKey,
|
||||
FactoryClassMethodKey,
|
||||
ExtraModuleDefinitionOptions
|
||||
>(this.options, this as any);
|
||||
builder.extras = extras;
|
||||
builder.transformModuleDefinition = transformDefinition;
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamic modules must expose public static methods that let you pass in
|
||||
* configuration parameters (control the module's behavior from the outside).
|
||||
* Some frequently used names that you may have seen in other modules are:
|
||||
* "forRoot", "forFeature", "register", "configure".
|
||||
*
|
||||
* This method "setClassMethodName" lets you specify the name of the
|
||||
* method that will be auto-generated.
|
||||
*
|
||||
* @param key name of the method
|
||||
*/
|
||||
setClassMethodName<StaticMethodKey extends string>(key: StaticMethodKey) {
|
||||
const builder = new ConfigurableModuleBuilder<
|
||||
ModuleOptions,
|
||||
StaticMethodKey,
|
||||
FactoryClassMethodKey,
|
||||
ExtraModuleDefinitionOptions
|
||||
>(this.options, this as any);
|
||||
builder.staticMethodKey = key;
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously configured modules (that rely on other modules, i.e. "ConfigModule")
|
||||
* let you pass the configuration factory class that will be registered and instantiated as a provider.
|
||||
* This provider then will be used to retrieve the module's configuration. To provide the configuration,
|
||||
* the corresponding factory method must be implemented.
|
||||
*
|
||||
* This method ("setFactoryMethodName") lets you control what method name will have to be
|
||||
* implemented by the config factory (default is "create").
|
||||
*
|
||||
* @param key name of the method
|
||||
*/
|
||||
setFactoryMethodName<FactoryClassMethodKey extends string>(
|
||||
key: FactoryClassMethodKey,
|
||||
) {
|
||||
const builder = new ConfigurableModuleBuilder<
|
||||
ModuleOptions,
|
||||
StaticMethodKey,
|
||||
FactoryClassMethodKey,
|
||||
ExtraModuleDefinitionOptions
|
||||
>(this.options, this as any);
|
||||
builder.factoryClassMethodKey = key;
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object consisting of multiple properties that lets you
|
||||
* easily construct dynamic configurable modules. See "ConfigurableModuleHost" interface for more details.
|
||||
*/
|
||||
build(): ConfigurableModuleHost<
|
||||
ModuleOptions,
|
||||
StaticMethodKey,
|
||||
FactoryClassMethodKey,
|
||||
ExtraModuleDefinitionOptions
|
||||
> {
|
||||
this.staticMethodKey ??= DEFAULT_METHOD_KEY as StaticMethodKey;
|
||||
this.factoryClassMethodKey ??=
|
||||
DEFAULT_FACTORY_CLASS_METHOD_KEY as FactoryClassMethodKey;
|
||||
this.options.optionsInjectionToken ??= this.options.moduleName
|
||||
? this.constructInjectionTokenString()
|
||||
: generateOptionsInjectionToken();
|
||||
this.transformModuleDefinition ??= definition => definition;
|
||||
|
||||
return {
|
||||
ConfigurableModuleClass:
|
||||
this.createConfigurableModuleCls<ModuleOptions>(),
|
||||
MODULE_OPTIONS_TOKEN: this.options.optionsInjectionToken,
|
||||
ASYNC_OPTIONS_TYPE: this.createTypeProxy('ASYNC_OPTIONS_TYPE'),
|
||||
OPTIONS_TYPE: this.createTypeProxy('OPTIONS_TYPE'),
|
||||
};
|
||||
}
|
||||
|
||||
private constructInjectionTokenString(): string {
|
||||
const moduleNameInSnakeCase = this.options.moduleName
|
||||
.trim()
|
||||
.split(/(?=[A-Z])/)
|
||||
.join('_')
|
||||
.toUpperCase();
|
||||
return `${moduleNameInSnakeCase}_MODULE_OPTIONS`;
|
||||
}
|
||||
|
||||
private createConfigurableModuleCls<ModuleOptions>(): ConfigurableModuleCls<
|
||||
ModuleOptions,
|
||||
StaticMethodKey,
|
||||
FactoryClassMethodKey
|
||||
> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const self = this;
|
||||
const asyncMethodKey = this.staticMethodKey + ASYNC_METHOD_SUFFIX;
|
||||
|
||||
class InternalModuleClass {
|
||||
static [self.staticMethodKey](
|
||||
options: ModuleOptions & ExtraModuleDefinitionOptions,
|
||||
): DynamicModule {
|
||||
const providers = [
|
||||
{
|
||||
provide: self.options.optionsInjectionToken,
|
||||
useValue: this.omitExtras(options, self.extras),
|
||||
},
|
||||
];
|
||||
if (self.options.alwaysTransient) {
|
||||
providers.push({
|
||||
provide: CONFIGURABLE_MODULE_ID,
|
||||
useValue: randomStringGenerator(),
|
||||
});
|
||||
}
|
||||
return self.transformModuleDefinition(
|
||||
{
|
||||
module: this,
|
||||
providers,
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
static [asyncMethodKey](
|
||||
options: ConfigurableModuleAsyncOptions<ModuleOptions> &
|
||||
ExtraModuleDefinitionOptions,
|
||||
): DynamicModule {
|
||||
const providers = this.createAsyncProviders(options);
|
||||
if (self.options.alwaysTransient) {
|
||||
providers.push({
|
||||
provide: CONFIGURABLE_MODULE_ID,
|
||||
useValue: randomStringGenerator(),
|
||||
});
|
||||
}
|
||||
return self.transformModuleDefinition(
|
||||
{
|
||||
module: this,
|
||||
imports: options.imports || [],
|
||||
providers,
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
private static omitExtras(
|
||||
input: ModuleOptions & ExtraModuleDefinitionOptions,
|
||||
extras: ExtraModuleDefinitionOptions | undefined,
|
||||
): ModuleOptions {
|
||||
if (!extras) {
|
||||
return input;
|
||||
}
|
||||
const moduleOptions = {};
|
||||
const extrasKeys = Object.keys(extras);
|
||||
|
||||
Object.keys(input)
|
||||
.filter(key => !extrasKeys.includes(key))
|
||||
.forEach(key => {
|
||||
moduleOptions[key] = input[key];
|
||||
});
|
||||
return moduleOptions as ModuleOptions;
|
||||
}
|
||||
|
||||
private static createAsyncProviders(
|
||||
options: ConfigurableModuleAsyncOptions<ModuleOptions>,
|
||||
): Provider[] {
|
||||
if (options.useExisting || options.useFactory) {
|
||||
return [this.createAsyncOptionsProvider(options)];
|
||||
}
|
||||
return [
|
||||
this.createAsyncOptionsProvider(options),
|
||||
{
|
||||
provide: options.useClass,
|
||||
useClass: options.useClass,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private static createAsyncOptionsProvider(
|
||||
options: ConfigurableModuleAsyncOptions<ModuleOptions>,
|
||||
): Provider {
|
||||
if (options.useFactory) {
|
||||
return {
|
||||
provide: self.options.optionsInjectionToken,
|
||||
useFactory: options.useFactory,
|
||||
inject: options.inject || [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
provide: self.options.optionsInjectionToken,
|
||||
useFactory: async (
|
||||
optionsFactory: ConfigurableModuleOptionsFactory<
|
||||
ModuleOptions,
|
||||
FactoryClassMethodKey
|
||||
>,
|
||||
) =>
|
||||
await optionsFactory[
|
||||
self.factoryClassMethodKey as keyof typeof optionsFactory
|
||||
](),
|
||||
inject: [options.useExisting || options.useClass],
|
||||
};
|
||||
}
|
||||
}
|
||||
return InternalModuleClass as unknown as ConfigurableModuleCls<
|
||||
ModuleOptions,
|
||||
StaticMethodKey,
|
||||
FactoryClassMethodKey
|
||||
>;
|
||||
}
|
||||
|
||||
private createTypeProxy(
|
||||
typeName: 'OPTIONS_TYPE' | 'ASYNC_OPTIONS_TYPE' | 'OptionsFactoryInterface',
|
||||
) {
|
||||
const proxy = new Proxy(
|
||||
{},
|
||||
{
|
||||
get: () => {
|
||||
throw new Error(
|
||||
`"${typeName}" is not supposed to be used as a value.`,
|
||||
);
|
||||
},
|
||||
},
|
||||
);
|
||||
return proxy as any;
|
||||
}
|
||||
}
|
||||
5
packages/common/module-utils/constants.ts
Normal file
5
packages/common/module-utils/constants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const DEFAULT_METHOD_KEY = 'register';
|
||||
export const DEFAULT_FACTORY_CLASS_METHOD_KEY = 'create';
|
||||
|
||||
export const ASYNC_METHOD_SUFFIX = 'Async';
|
||||
export const CONFIGURABLE_MODULE_ID = 'CONFIGURABLE_MODULE_ID';
|
||||
2
packages/common/module-utils/index.ts
Normal file
2
packages/common/module-utils/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './configurable-module.builder';
|
||||
export * from './interfaces';
|
||||
@@ -0,0 +1,51 @@
|
||||
import { FactoryProvider, ModuleMetadata, Type } from '../../interfaces';
|
||||
import { DEFAULT_FACTORY_CLASS_METHOD_KEY } from '../constants';
|
||||
|
||||
/**
|
||||
* Interface that must be implemented by the module options factory class.
|
||||
* Method key varies depending on the "FactoryClassMethodKey" type argument.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export type ConfigurableModuleOptionsFactory<
|
||||
ModuleOptions,
|
||||
FactoryClassMethodKey extends string,
|
||||
> = Record<
|
||||
`${FactoryClassMethodKey}`,
|
||||
() => Promise<ModuleOptions> | ModuleOptions
|
||||
>;
|
||||
|
||||
/**
|
||||
* Interface that represents the module async options object
|
||||
* Factory method name varies depending on the "FactoryClassMethodKey" type argument.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ConfigurableModuleAsyncOptions<
|
||||
ModuleOptions,
|
||||
FactoryClassMethodKey extends string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
> extends Pick<ModuleMetadata, 'imports'> {
|
||||
/**
|
||||
* Injection token resolving to an existing provider. The provider must implement
|
||||
* the corresponding interface.
|
||||
*/
|
||||
useExisting?: Type<
|
||||
ConfigurableModuleOptionsFactory<ModuleOptions, FactoryClassMethodKey>
|
||||
>;
|
||||
/**
|
||||
* Injection token resolving to a class that will be instantiated as a provider.
|
||||
* The class must implement the corresponding interface.
|
||||
*/
|
||||
useClass?: Type<
|
||||
ConfigurableModuleOptionsFactory<ModuleOptions, FactoryClassMethodKey>
|
||||
>;
|
||||
/**
|
||||
* Function returning options (or a Promise resolving to options) to configure the
|
||||
* cache module.
|
||||
*/
|
||||
useFactory?: (...args: unknown[]) => Promise<ModuleOptions> | ModuleOptions;
|
||||
/**
|
||||
* Dependencies that a Factory may inject.
|
||||
*/
|
||||
inject?: FactoryProvider['inject'];
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { DynamicModule } from '../../interfaces';
|
||||
import {
|
||||
DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
DEFAULT_METHOD_KEY,
|
||||
} from '../constants';
|
||||
import { ConfigurableModuleAsyncOptions } from './configurable-module-async-options.interface';
|
||||
|
||||
/**
|
||||
* Class that represents a blueprint/prototype for a configurable Nest module.
|
||||
* This class provides static methods for constructing dynamic modules. Their names
|
||||
* can be controlled through the "MethodKey" type argument.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export type ConfigurableModuleCls<
|
||||
ModuleOptions,
|
||||
MethodKey extends string = typeof DEFAULT_METHOD_KEY,
|
||||
FactoryClassMethodKey extends string = typeof DEFAULT_FACTORY_CLASS_METHOD_KEY,
|
||||
ExtraModuleDefinitionOptions = {},
|
||||
> = {
|
||||
new (): any;
|
||||
} & Record<
|
||||
`${MethodKey}`,
|
||||
(options: ModuleOptions & ExtraModuleDefinitionOptions) => DynamicModule
|
||||
> &
|
||||
Record<
|
||||
`${MethodKey}Async`,
|
||||
(
|
||||
options: ConfigurableModuleAsyncOptions<
|
||||
ModuleOptions,
|
||||
FactoryClassMethodKey
|
||||
> &
|
||||
ExtraModuleDefinitionOptions,
|
||||
) => DynamicModule
|
||||
>;
|
||||
@@ -0,0 +1,77 @@
|
||||
import { ConfigurableModuleAsyncOptions } from './configurable-module-async-options.interface';
|
||||
import { ConfigurableModuleCls } from './configurable-module-cls.interface';
|
||||
|
||||
/**
|
||||
* Configurable module host. See properties for more details
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export interface ConfigurableModuleHost<
|
||||
ModuleOptions = Record<string, unknown>,
|
||||
MethodKey extends string = string,
|
||||
FactoryClassMethodKey extends string = string,
|
||||
ExtraModuleDefinitionOptions = {},
|
||||
> {
|
||||
/**
|
||||
* Class that represents a blueprint/prototype for a configurable Nest module.
|
||||
* This class provides static methods for constructing dynamic modules. Their names
|
||||
* can be controlled through the "MethodKey" type argument.
|
||||
*
|
||||
* Your module class should inherit from this class to make the static methods available.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @Module({})
|
||||
* class IntegrationModule extends ConfigurableModuleCls {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
ConfigurableModuleClass: ConfigurableModuleCls<
|
||||
ModuleOptions,
|
||||
MethodKey,
|
||||
FactoryClassMethodKey,
|
||||
ExtraModuleDefinitionOptions
|
||||
>;
|
||||
/**
|
||||
* Module options provider token. Can be used to inject the "options object" to
|
||||
* providers registered within the host module.
|
||||
*/
|
||||
MODULE_OPTIONS_TOKEN: string | symbol;
|
||||
/**
|
||||
* Can be used to auto-infer the compound "async module options" type.
|
||||
* Note: this property is not supposed to be used as a value.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @Module({})
|
||||
* class IntegrationModule extends ConfigurableModuleCls {
|
||||
* static module = initializer(IntegrationModule);
|
||||
*
|
||||
* static registerAsync(options: typeof ASYNC_OPTIONS_TYPE): DynamicModule {
|
||||
* return super.registerAsync(options);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
ASYNC_OPTIONS_TYPE: ConfigurableModuleAsyncOptions<
|
||||
ModuleOptions,
|
||||
FactoryClassMethodKey
|
||||
> &
|
||||
ExtraModuleDefinitionOptions;
|
||||
/**
|
||||
* Can be used to auto-infer the compound "module options" type (options interface + extra module definition options).
|
||||
* Note: this property is not supposed to be used as a value.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @Module({})
|
||||
* class IntegrationModule extends ConfigurableModuleCls {
|
||||
* static module = initializer(IntegrationModule);
|
||||
*
|
||||
* static register(options: typeof OPTIONS_TYPE): DynamicModule {
|
||||
* return super.register(options);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
OPTIONS_TYPE: ModuleOptions & ExtraModuleDefinitionOptions;
|
||||
}
|
||||
3
packages/common/module-utils/interfaces/index.ts
Normal file
3
packages/common/module-utils/interfaces/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './configurable-module-async-options.interface';
|
||||
export * from './configurable-module-cls.interface';
|
||||
export * from './configurable-module-host.interface';
|
||||
@@ -0,0 +1,6 @@
|
||||
import { randomStringGenerator } from '../../utils/random-string-generator.util';
|
||||
|
||||
export function generateOptionsInjectionToken() {
|
||||
const hash = randomStringGenerator();
|
||||
return `CONFIGURABLE_MODULE_OPTIONS[${hash}]`;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nestjs/common",
|
||||
"version": "8.3.1",
|
||||
"version": "8.4.5",
|
||||
"description": "Nest - modern, fast, powerful node.js web framework (@common)",
|
||||
"author": "Kamil Mysliwiec",
|
||||
"homepage": "https://nestjs.com",
|
||||
@@ -17,9 +17,9 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "0.26.0",
|
||||
"axios": "0.27.2",
|
||||
"iterare": "1.2.1",
|
||||
"tslib": "2.3.1",
|
||||
"tslib": "2.4.0",
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { isNil, isNumber } from '../utils/shared.utils';
|
||||
export class DefaultValuePipe<T = any, R = any>
|
||||
implements PipeTransform<T, T | R>
|
||||
{
|
||||
constructor(private readonly defaultValue: R) {}
|
||||
constructor(protected readonly defaultValue: R) {}
|
||||
|
||||
transform(value?: T, _metadata?: ArgumentMetadata): T | R {
|
||||
if (
|
||||
|
||||
@@ -36,7 +36,7 @@ export class ParseArrayPipe implements PipeTransform {
|
||||
protected readonly validationPipe: ValidationPipe;
|
||||
protected exceptionFactory: (error: string) => any;
|
||||
|
||||
constructor(@Optional() private readonly options: ParseArrayOptions = {}) {
|
||||
constructor(@Optional() protected readonly options: ParseArrayOptions = {}) {
|
||||
this.validationPipe = new ValidationPipe({
|
||||
transform: true,
|
||||
validateCustomDecorators: true,
|
||||
|
||||
@@ -48,14 +48,32 @@ export class ParseBoolPipe
|
||||
value: string | boolean,
|
||||
metadata: ArgumentMetadata,
|
||||
): Promise<boolean> {
|
||||
if (value === true || value === 'true') {
|
||||
if (this.isTrue(value)) {
|
||||
return true;
|
||||
}
|
||||
if (value === false || value === 'false') {
|
||||
if (this.isFalse(value)) {
|
||||
return false;
|
||||
}
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (boolean string is expected)',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value currently processed route argument
|
||||
* @returns `true` if `value` is said 'true', ie., if it is equal to the boolean
|
||||
* `true` or the string `"true"`
|
||||
*/
|
||||
protected isTrue(value: string | boolean): boolean {
|
||||
return value === true || value === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value currently processed route argument
|
||||
* @returns `true` if `value` is said 'false', ie., if it is equal to the boolean
|
||||
* `false` or the string `"false"`
|
||||
*/
|
||||
protected isFalse(value: string | boolean): boolean {
|
||||
return value === false || value === 'false';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,15 +39,23 @@ export class ParseFloatPipe implements PipeTransform<string> {
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
|
||||
const isNumeric =
|
||||
['string', 'number'].includes(typeof value) &&
|
||||
!isNaN(parseFloat(value)) &&
|
||||
isFinite(value as any);
|
||||
if (!isNumeric) {
|
||||
if (!this.isNumeric(value)) {
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (numeric string is expected)',
|
||||
);
|
||||
}
|
||||
return parseFloat(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value currently processed route argument
|
||||
* @returns `true` if `value` is a valid float number
|
||||
*/
|
||||
protected isNumeric(value: string): boolean {
|
||||
return (
|
||||
['string', 'number'].includes(typeof value) &&
|
||||
!isNaN(parseFloat(value)) &&
|
||||
isFinite(value as any)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,15 +44,23 @@ export class ParseIntPipe implements PipeTransform<string> {
|
||||
* @param metadata contains metadata about the currently processed route argument
|
||||
*/
|
||||
async transform(value: string, metadata: ArgumentMetadata): Promise<number> {
|
||||
const isNumeric =
|
||||
['string', 'number'].includes(typeof value) &&
|
||||
/^-?\d+$/.test(value) &&
|
||||
isFinite(value as any);
|
||||
if (!isNumeric) {
|
||||
if (!this.isNumeric(value)) {
|
||||
throw this.exceptionFactory(
|
||||
'Validation failed (numeric string is expected)',
|
||||
);
|
||||
}
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value currently processed route argument
|
||||
* @returns `true` if `value` is a valid integer number
|
||||
*/
|
||||
protected isNumeric(value: string): boolean {
|
||||
return (
|
||||
['string', 'number'].includes(typeof value) &&
|
||||
/^-?\d+$/.test(value) &&
|
||||
isFinite(value as any)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
ErrorHttpStatusCode,
|
||||
HttpErrorByCode,
|
||||
} from '../utils/http-error-by-code.util';
|
||||
import { isUUID } from '../utils/is-uuid';
|
||||
import { isString } from '../utils/shared.utils';
|
||||
|
||||
export interface ParseUUIDPipeOptions {
|
||||
version?: '3' | '4' | '5';
|
||||
@@ -19,6 +19,12 @@ export interface ParseUUIDPipeOptions {
|
||||
|
||||
@Injectable()
|
||||
export class ParseUUIDPipe implements PipeTransform<string> {
|
||||
protected static uuidRegExps = {
|
||||
3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
|
||||
4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
|
||||
5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
|
||||
all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
|
||||
};
|
||||
private readonly version: '3' | '4' | '5';
|
||||
protected exceptionFactory: (errors: string) => any;
|
||||
|
||||
@@ -35,14 +41,23 @@ export class ParseUUIDPipe implements PipeTransform<string> {
|
||||
exceptionFactory ||
|
||||
(error => new HttpErrorByCode[errorHttpStatusCode](error));
|
||||
}
|
||||
|
||||
async transform(value: string, metadata: ArgumentMetadata): Promise<string> {
|
||||
if (!isUUID(value, this.version)) {
|
||||
if (!this.isUUID(value, this.version)) {
|
||||
throw this.exceptionFactory(
|
||||
`Validation failed (uuid ${
|
||||
this.version ? 'v' + this.version : ''
|
||||
`Validation failed (uuid${
|
||||
this.version ? ` v ${this.version}` : ''
|
||||
} is expected)`,
|
||||
);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
protected isUUID(str: unknown, version = 'all') {
|
||||
if (!isString(str)) {
|
||||
throw this.exceptionFactory('The value passed as UUID is not a string');
|
||||
}
|
||||
const pattern = ParseUUIDPipe.uuidRegExps[version];
|
||||
return pattern?.test(str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,27 +183,58 @@ export class ConsoleLogger implements LoggerService {
|
||||
logLevel: LogLevel = 'log',
|
||||
writeStreamType?: 'stdout' | 'stderr',
|
||||
) {
|
||||
const color = this.getColorByLogLevel(logLevel);
|
||||
messages.forEach(message => {
|
||||
const output = isPlainObject(message)
|
||||
? `${color('Object:')}\n${JSON.stringify(
|
||||
message,
|
||||
(key, value) =>
|
||||
typeof value === 'bigint' ? value.toString() : value,
|
||||
2,
|
||||
)}\n`
|
||||
: color(message as string);
|
||||
|
||||
const pidMessage = color(`[Nest] ${process.pid} - `);
|
||||
const pidMessage = this.formatPid(process.pid);
|
||||
const contextMessage = context ? yellow(`[${context}] `) : '';
|
||||
const timestampDiff = this.updateAndGetTimestampDiff();
|
||||
const formattedLogLevel = color(logLevel.toUpperCase().padStart(7, ' '));
|
||||
const computedMessage = `${pidMessage}${this.getTimestamp()} ${formattedLogLevel} ${contextMessage}${output}${timestampDiff}\n`;
|
||||
const formattedLogLevel = logLevel.toUpperCase().padStart(7, ' ');
|
||||
const formatedMessage = this.formatMessage(
|
||||
logLevel,
|
||||
message,
|
||||
pidMessage,
|
||||
formattedLogLevel,
|
||||
contextMessage,
|
||||
timestampDiff,
|
||||
);
|
||||
|
||||
process[writeStreamType ?? 'stdout'].write(computedMessage);
|
||||
process[writeStreamType ?? 'stdout'].write(formatedMessage);
|
||||
});
|
||||
}
|
||||
|
||||
protected formatPid(pid: number) {
|
||||
return `[Nest] ${pid} - `;
|
||||
}
|
||||
|
||||
protected formatMessage(
|
||||
logLevel: LogLevel,
|
||||
message: unknown,
|
||||
pidMessage: string,
|
||||
formattedLogLevel: string,
|
||||
contextMessage: string,
|
||||
timestampDiff: string,
|
||||
) {
|
||||
const output = this.stringifyMessage(message, logLevel);
|
||||
pidMessage = this.colorize(pidMessage, logLevel);
|
||||
formattedLogLevel = this.colorize(formattedLogLevel, logLevel);
|
||||
return `${pidMessage}${this.getTimestamp()} ${formattedLogLevel} ${contextMessage}${output}${timestampDiff}\n`;
|
||||
}
|
||||
|
||||
protected stringifyMessage(message: unknown, logLevel: LogLevel) {
|
||||
return isPlainObject(message)
|
||||
? `${this.colorize('Object:', logLevel)}\n${JSON.stringify(
|
||||
message,
|
||||
(key, value) =>
|
||||
typeof value === 'bigint' ? value.toString() : value,
|
||||
2,
|
||||
)}\n`
|
||||
: this.colorize(message as string, logLevel);
|
||||
}
|
||||
|
||||
protected colorize(message: string, logLevel: LogLevel) {
|
||||
const color = this.getColorByLogLevel(logLevel);
|
||||
return color(message);
|
||||
}
|
||||
|
||||
protected printStackTrace(stack: string) {
|
||||
if (!stack) {
|
||||
return;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { expect } from 'chai';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { HttpService } from '../../http/http.service';
|
||||
|
||||
describe('HttpService', () => {
|
||||
it('should not mutate user-given axios options object', done => {
|
||||
const http = new HttpService({ get: () => Promise.resolve() } as any);
|
||||
const options: AxiosRequestConfig = {};
|
||||
|
||||
lastValueFrom(http.get('/', options)).then(() => {
|
||||
expect(options.cancelToken).to.be.undefined;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
import { expect } from 'chai';
|
||||
import { Provider } from '../../interfaces';
|
||||
import { ConfigurableModuleBuilder } from '../../module-utils';
|
||||
|
||||
describe('ConfigurableModuleBuilder', () => {
|
||||
describe('setExtras', () => {
|
||||
it('should apply module definition transformer function and return typed builder', () => {
|
||||
const { ConfigurableModuleClass } = new ConfigurableModuleBuilder()
|
||||
.setExtras(
|
||||
{ isGlobal: false },
|
||||
(definition, extras: { isGlobal: boolean }) => ({
|
||||
...definition,
|
||||
global: extras.isGlobal,
|
||||
}),
|
||||
)
|
||||
.build();
|
||||
|
||||
expect(
|
||||
ConfigurableModuleClass.register({
|
||||
// No type error
|
||||
isGlobal: true,
|
||||
}),
|
||||
).to.deep.include({
|
||||
global: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('setClassMethodName', () => {
|
||||
it('should set static class method name and return typed builder', () => {
|
||||
const { ConfigurableModuleClass } = new ConfigurableModuleBuilder()
|
||||
.setClassMethodName('forRoot')
|
||||
.build();
|
||||
|
||||
expect(ConfigurableModuleClass.forRoot).to.not.be.undefined;
|
||||
expect(ConfigurableModuleClass.forRootAsync).to.not.be.undefined;
|
||||
expect((ConfigurableModuleClass as any).register).to.be.undefined;
|
||||
});
|
||||
});
|
||||
describe('setFactoryMethodName', () => {
|
||||
it('should set configuration factory class method name and return typed builder', () => {
|
||||
const { ConfigurableModuleClass } = new ConfigurableModuleBuilder()
|
||||
.setFactoryMethodName('createOptions')
|
||||
.build();
|
||||
|
||||
expect(
|
||||
ConfigurableModuleClass.registerAsync({
|
||||
useClass: class {
|
||||
// No type error
|
||||
createOptions() {}
|
||||
},
|
||||
}),
|
||||
).to.not.be.undefined;
|
||||
});
|
||||
});
|
||||
describe('build', () => {
|
||||
it('should return a fully typed "ConfigurableModuleClass"', () => {
|
||||
type ExtraConfig = { isGlobal?: boolean; extraProviders: Provider[] };
|
||||
|
||||
const {
|
||||
ConfigurableModuleClass,
|
||||
OPTIONS_TYPE,
|
||||
ASYNC_OPTIONS_TYPE,
|
||||
MODULE_OPTIONS_TOKEN,
|
||||
} = new ConfigurableModuleBuilder({
|
||||
moduleName: 'RandomTest',
|
||||
alwaysTransient: true,
|
||||
})
|
||||
.setFactoryMethodName('createOptions')
|
||||
.setClassMethodName('forFeature')
|
||||
.setExtras<ExtraConfig>(
|
||||
{ isGlobal: false, extraProviders: [] },
|
||||
(definition, extras) => ({
|
||||
...definition,
|
||||
global: extras.isGlobal,
|
||||
providers: definition.providers?.concat(extras.extraProviders),
|
||||
}),
|
||||
)
|
||||
.build();
|
||||
|
||||
const definition = ConfigurableModuleClass.forFeatureAsync({
|
||||
useFactory: () => {},
|
||||
isGlobal: true,
|
||||
extraProviders: ['test' as any],
|
||||
});
|
||||
|
||||
expect(definition.global).to.equal(true);
|
||||
expect(definition.providers).to.have.length(3);
|
||||
expect(definition.providers).to.deep.contain('test');
|
||||
expect(MODULE_OPTIONS_TOKEN).to.equal('RANDOM_TEST_MODULE_OPTIONS');
|
||||
expect((definition.providers[0] as any).provide).to.equal(
|
||||
'RANDOM_TEST_MODULE_OPTIONS',
|
||||
);
|
||||
|
||||
try {
|
||||
expect(ASYNC_OPTIONS_TYPE.imports).to.equal(undefined);
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(Error);
|
||||
expect(err.message).to.equal(
|
||||
'"ASYNC_OPTIONS_TYPE" is not supposed to be used as a value.',
|
||||
);
|
||||
}
|
||||
try {
|
||||
expect(OPTIONS_TYPE.isGlobal).to.equal(undefined);
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(Error);
|
||||
expect(err.message).to.equal(
|
||||
'"OPTIONS_TYPE" is not supposed to be used as a value.',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import { ArgumentMetadata } from '../../interfaces';
|
||||
import { ParseIntPipe } from '../../pipes/parse-int.pipe';
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { expect } from 'chai';
|
||||
import { HttpStatus } from '../../enums';
|
||||
import { HttpException } from '../../exceptions';
|
||||
import { ArgumentMetadata } from '../../interfaces';
|
||||
import { ParseUUIDPipe } from '../../pipes/parse-uuid.pipe';
|
||||
|
||||
class TestException extends HttpException {
|
||||
constructor() {
|
||||
super('This is a TestException', HttpStatus.I_AM_A_TEAPOT);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ParseUUIDPipe', () => {
|
||||
let target: ParseUUIDPipe;
|
||||
const exceptionFactory = (error: any) => new TestException();
|
||||
|
||||
describe('transform', () => {
|
||||
const v3 = 'e8b5a51d-11c8-3310-a6ab-367563f20686';
|
||||
@@ -12,53 +21,80 @@ describe('ParseUUIDPipe', () => {
|
||||
|
||||
describe('when validation passes', () => {
|
||||
it('should return string if value is uuid v3, v4 or v5', async () => {
|
||||
target = new ParseUUIDPipe();
|
||||
target = new ParseUUIDPipe({ exceptionFactory });
|
||||
expect(await target.transform(v3, {} as ArgumentMetadata)).to.equal(v3);
|
||||
expect(await target.transform(v4, {} as ArgumentMetadata)).to.equal(v4);
|
||||
expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5);
|
||||
});
|
||||
|
||||
it('should return string if value is uuid v3', async () => {
|
||||
target = new ParseUUIDPipe({ version: '3' });
|
||||
target = new ParseUUIDPipe({ version: '3', exceptionFactory });
|
||||
expect(await target.transform(v3, {} as ArgumentMetadata)).to.equal(v3);
|
||||
});
|
||||
|
||||
it('should return string if value is uuid v4', async () => {
|
||||
target = new ParseUUIDPipe({ version: '4' });
|
||||
target = new ParseUUIDPipe({ version: '4', exceptionFactory });
|
||||
expect(await target.transform(v4, {} as ArgumentMetadata)).to.equal(v4);
|
||||
});
|
||||
|
||||
it('should return string if value is uuid v5', async () => {
|
||||
target = new ParseUUIDPipe({ version: '5' });
|
||||
target = new ParseUUIDPipe({ version: '5', exceptionFactory });
|
||||
expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when validation fails', () => {
|
||||
it('should throw an error', async () => {
|
||||
target = new ParseUUIDPipe();
|
||||
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
|
||||
target = new ParseUUIDPipe({ exceptionFactory });
|
||||
await expect(
|
||||
target.transform('123a', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
});
|
||||
|
||||
it('should throw an error - not a string', async () => {
|
||||
target = new ParseUUIDPipe({ exceptionFactory });
|
||||
await expect(
|
||||
target.transform(undefined, {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
});
|
||||
|
||||
it('should throw an error - v3', async () => {
|
||||
target = new ParseUUIDPipe({ version: '3' });
|
||||
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
|
||||
expect(target.transform(v4, {} as ArgumentMetadata)).to.be.rejected;
|
||||
expect(target.transform(v5, {} as ArgumentMetadata)).to.be.rejected;
|
||||
target = new ParseUUIDPipe({ version: '3', exceptionFactory });
|
||||
await expect(
|
||||
target.transform('123a', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
await expect(
|
||||
target.transform(v4, {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
await expect(
|
||||
target.transform(v5, {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
});
|
||||
|
||||
it('should throw an error - v4', async () => {
|
||||
target = new ParseUUIDPipe({ version: '4' });
|
||||
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
|
||||
expect(target.transform(v3, {} as ArgumentMetadata)).to.be.rejected;
|
||||
expect(target.transform(v5, {} as ArgumentMetadata)).to.be.rejected;
|
||||
target = new ParseUUIDPipe({ version: '4', exceptionFactory });
|
||||
await expect(
|
||||
target.transform('123a', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
await expect(
|
||||
target.transform(v3, {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
await expect(
|
||||
target.transform(v5, {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
});
|
||||
|
||||
it('should throw an error - v5 ', async () => {
|
||||
target = new ParseUUIDPipe({ version: '5' });
|
||||
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
|
||||
expect(target.transform(v3, {} as ArgumentMetadata)).to.be.rejected;
|
||||
expect(target.transform(v4, {} as ArgumentMetadata)).to.be.rejected;
|
||||
target = new ParseUUIDPipe({ version: '5', exceptionFactory });
|
||||
await expect(
|
||||
target.transform('123a', {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
await expect(
|
||||
target.transform(v3, {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
await expect(
|
||||
target.transform(v4, {} as ArgumentMetadata),
|
||||
).to.be.rejectedWith(TestException);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
import 'reflect-metadata';
|
||||
import * as sinon from 'sinon';
|
||||
import { ConsoleLogger, Logger, LoggerService } from '../../services';
|
||||
import { ConsoleLogger, Logger, LoggerService, LogLevel } from '../../services';
|
||||
|
||||
describe('Logger', () => {
|
||||
describe('[static methods]', () => {
|
||||
@@ -520,4 +520,61 @@ describe('Logger', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('ConsoleLogger', () => {
|
||||
let processStdoutWriteSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
processStdoutWriteSpy = sinon.spy(process.stdout, 'write');
|
||||
});
|
||||
afterEach(() => {
|
||||
processStdoutWriteSpy.restore();
|
||||
});
|
||||
|
||||
it('should support custom formatter', () => {
|
||||
class CustomConsoleLogger extends ConsoleLogger {
|
||||
protected formatMessage(
|
||||
logLevel: LogLevel,
|
||||
message: unknown,
|
||||
pidMessage: string,
|
||||
formattedLogLevel: string,
|
||||
contextMessage: string,
|
||||
timestampDiff: string,
|
||||
) {
|
||||
return `Prefix: ${message}`;
|
||||
}
|
||||
}
|
||||
|
||||
const consoleLogger = new CustomConsoleLogger();
|
||||
consoleLogger.debug('test');
|
||||
|
||||
expect(processStdoutWriteSpy.firstCall.firstArg).to.equal(`Prefix: test`);
|
||||
});
|
||||
|
||||
it('should support custom formatter and colorizer', () => {
|
||||
class CustomConsoleLogger extends ConsoleLogger {
|
||||
protected formatMessage(
|
||||
logLevel: LogLevel,
|
||||
message: unknown,
|
||||
pidMessage: string,
|
||||
formattedLogLevel: string,
|
||||
contextMessage: string,
|
||||
timestampDiff: string,
|
||||
) {
|
||||
const strMessage = this.stringifyMessage(message, logLevel);
|
||||
return `Prefix: ${strMessage}`;
|
||||
}
|
||||
|
||||
protected colorize(message: string, logLevel: LogLevel): string {
|
||||
return `~~~${message}~~~`;
|
||||
}
|
||||
}
|
||||
|
||||
const consoleLogger = new CustomConsoleLogger();
|
||||
consoleLogger.debug('test');
|
||||
|
||||
expect(processStdoutWriteSpy.firstCall.firstArg).to.equal(
|
||||
`Prefix: ~~~test~~~`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { BadRequestException } from '../exceptions';
|
||||
import { isString } from './shared.utils';
|
||||
|
||||
const uuid = {
|
||||
3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
|
||||
4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
|
||||
5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
|
||||
all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
|
||||
};
|
||||
|
||||
export function isUUID(str: any, version = 'all') {
|
||||
if (!isString(str)) {
|
||||
throw new BadRequestException('The value passed as UUID is not a string');
|
||||
}
|
||||
const pattern = uuid[version];
|
||||
return pattern && pattern.test(str);
|
||||
}
|
||||
@@ -54,7 +54,7 @@ With official support, you can get expert help straight from Nest core team. We
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support from the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsors
|
||||
<table style="text-align:center;"><tr>
|
||||
@@ -64,17 +64,20 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://careers.labster.com/departments/platform" target="_blank"><img src="https://nestjs.com/img/labster-logo.png" width="170" valign="middle" /></a></td><td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="150" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td></</tr></table>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="110" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td></</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ export abstract class AbstractHttpAdapter<
|
||||
abstract setErrorHandler(handler: Function, prefix?: string);
|
||||
abstract setNotFoundHandler(handler: Function, prefix?: string);
|
||||
abstract setHeader(response, name: string, value: string);
|
||||
abstract registerParserMiddleware(prefix?: string);
|
||||
abstract registerParserMiddleware(prefix?: string, rawBody?: boolean);
|
||||
abstract enableCors(
|
||||
options: CorsOptions | CorsOptionsDelegate<TRequest>,
|
||||
prefix?: string,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user