mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
Merge branch 'master' into feature/grpc-multipackage
# Conflicts: # integration/microservices/src/grpc/grpc.controller.ts # packages/microservices/client/client-grpc.ts # packages/microservices/server/server-grpc.ts
This commit is contained in:
178
.circleci/config.yml
Normal file
178
.circleci/config.yml
Normal file
@@ -0,0 +1,178 @@
|
||||
version: 2
|
||||
|
||||
aliases:
|
||||
- &restore-cache
|
||||
restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- &install-deps
|
||||
run:
|
||||
name: Install dependencies
|
||||
command: npm ci
|
||||
- &build-packages
|
||||
run:
|
||||
name: Build
|
||||
command: npm run build
|
||||
- &run-unit-tests
|
||||
run:
|
||||
name: Test
|
||||
command: npm run test
|
||||
|
||||
unit-tests-template: &unit-tests-template
|
||||
working_directory: ~/nest
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- *run-unit-tests
|
||||
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Update NPM version
|
||||
command: 'sudo npm install -g npm@latest'
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: npm ci
|
||||
- save_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ./node_modules
|
||||
- run:
|
||||
name: Build
|
||||
command: npm run build
|
||||
|
||||
test_node_12:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- *run-unit-tests
|
||||
- run:
|
||||
name: Collect coverage
|
||||
command: npm run coverage
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
|
||||
test_node_10:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: circleci/node:10
|
||||
|
||||
test_node_8:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
|
||||
lint:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- run:
|
||||
name: Lint
|
||||
command: npm run lint
|
||||
|
||||
integration_tests:
|
||||
working_directory: ~/nest
|
||||
machine: true
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Prepare nvm
|
||||
command: |
|
||||
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
|
||||
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
|
||||
- run:
|
||||
name: Upgrade Node.js
|
||||
command: |
|
||||
nvm install v12
|
||||
node -v
|
||||
nvm alias default v12
|
||||
- run:
|
||||
name: Install Docker Compose
|
||||
command: |
|
||||
curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
|
||||
chmod +x ~/docker-compose
|
||||
sudo mv ~/docker-compose /usr/local/bin/docker-compose
|
||||
- *install-deps
|
||||
- run:
|
||||
name: Prepare tests
|
||||
command: |
|
||||
bash ./scripts/prepare.sh
|
||||
sleep 10
|
||||
- run:
|
||||
name: List containers
|
||||
command: docker ps
|
||||
- run:
|
||||
name: Integration tests
|
||||
command: npm run integration-test
|
||||
|
||||
codechecks_benchmarks:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- run:
|
||||
name: Install native wrk
|
||||
command: .circleci/install-wrk.sh
|
||||
- run:
|
||||
name: Run codechecks with benchmarks
|
||||
command: yarn codechecks:benchmarks
|
||||
|
||||
samples:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: circleci/node:12
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- run:
|
||||
name: Build all samples
|
||||
command: npm run build:samples
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build
|
||||
- test_node_12:
|
||||
requires:
|
||||
- build
|
||||
- test_node_10:
|
||||
requires:
|
||||
- build
|
||||
- test_node_8:
|
||||
requires:
|
||||
- build
|
||||
- lint:
|
||||
requires:
|
||||
- build
|
||||
- integration_tests:
|
||||
requires:
|
||||
- build
|
||||
- samples:
|
||||
requires:
|
||||
- build
|
||||
- codechecks_benchmarks:
|
||||
requires:
|
||||
- build
|
||||
13
.circleci/install-wrk.sh
Executable file
13
.circleci/install-wrk.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# based on https://medium.com/@felipedutratine/intelligent-benchmark-with-wrk-163986c1587f
|
||||
|
||||
cd /tmp/
|
||||
sudo apt-get install build-essential libssl-dev git -y
|
||||
git clone https://github.com/wg/wrk.git wrk
|
||||
cd wrk
|
||||
sudo make
|
||||
# move the executable to somewhere in your PATH, ex:
|
||||
sudo cp wrk /usr/local/bin
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: nest
|
||||
46
.github/ISSUE_TEMPLATE.md
vendored
46
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,46 +0,0 @@
|
||||
<!--
|
||||
PLEASE HELP US PROCESS GITHUB ISSUES FASTER BY PROVIDING THE FOLLOWING INFORMATION.
|
||||
|
||||
ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION.
|
||||
-->
|
||||
|
||||
## I'm submitting a...
|
||||
<!--
|
||||
Please search GitHub for a similar issue or PR before submitting.
|
||||
Check one of the following options with "x" -->
|
||||
<pre><code>
|
||||
[ ] Regression <!--(a behavior that used to work and stopped working in a new release)-->
|
||||
[ ] Bug report
|
||||
[ ] Feature request
|
||||
[ ] Documentation issue or request
|
||||
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
|
||||
</code></pre>
|
||||
|
||||
## Current behavior
|
||||
<!-- Describe how the issue manifests. -->
|
||||
|
||||
|
||||
## Expected behavior
|
||||
<!-- Describe what the desired behavior would be. -->
|
||||
|
||||
|
||||
## Minimal reproduction of the problem with instructions
|
||||
<!-- Please share a repo, a gist, or step-by-step instructions. -->
|
||||
|
||||
## What is the motivation / use case for changing the behavior?
|
||||
<!-- Describe the motivation or the concrete use case. -->
|
||||
|
||||
|
||||
## Environment
|
||||
|
||||
<pre><code>
|
||||
Nest version: X.Y.Z
|
||||
<!-- Check whether this is still an issue in the most recent Nest version -->
|
||||
|
||||
For Tooling issues:
|
||||
- Node version: XX <!-- run `node --version` -->
|
||||
- Platform: <!-- Mac, Linux, Windows -->
|
||||
|
||||
Others:
|
||||
<!-- Anything else relevant? Operating system version, IDE, package manager, ... -->
|
||||
</code></pre>
|
||||
41
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: "\U0001F41B Bug Report"
|
||||
about: "If something isn't working as expected \U0001F914."
|
||||
title: ''
|
||||
labels: 'type: potential issue :broken_heart:,needs triage'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Bug Report
|
||||
|
||||
## Current behavior
|
||||
<!-- Describe how the issue manifests. -->
|
||||
|
||||
## Input Code
|
||||
<!-- REPL or Repo link if applicable: -->
|
||||
|
||||
```ts
|
||||
const your = (code) => here;
|
||||
```
|
||||
|
||||
## Expected behavior
|
||||
<!-- A clear and concise description of what you expected to happen (or code). -->
|
||||
|
||||
## Possible Solution
|
||||
<!--- Only if you have suggestions on a fix for the bug -->
|
||||
|
||||
## Environment
|
||||
|
||||
<pre><code>
|
||||
Nest version: X.Y.Z
|
||||
<!-- Check whether this is still an issue in the most recent Nest version -->
|
||||
|
||||
For Tooling issues:
|
||||
- Node version: XX <!-- run `node --version` -->
|
||||
- Platform: <!-- Mac, Linux, Windows -->
|
||||
|
||||
Others:
|
||||
<!-- Anything else relevant? Operating system version, IDE, package manager, ... -->
|
||||
</code></pre>
|
||||
|
||||
22
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
22
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: "\U0001F680 Feature Request"
|
||||
about: "I have a suggestion \U0001F63B!"
|
||||
title: ''
|
||||
labels: 'type: enhancement :wolf:,needs triage'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Feature Request
|
||||
|
||||
## Is your feature request related to a problem? Please describe.
|
||||
<!-- A clear and concise description of what the problem is. Ex. I have an issue when [...] -->
|
||||
|
||||
## Describe the solution you'd like
|
||||
<!-- A clear and concise description of what you want to happen. Add any considered drawbacks. -->
|
||||
|
||||
## Teachability, Documentation, Adoption, Migration Strategy
|
||||
<!-- If you can, explain how users will be able to use this and possibly write out a version the docs. Maybe a screenshot or design? -->
|
||||
|
||||
## What is the motivation / use case for changing the behavior?
|
||||
<!-- Describe the motivation or the concrete use case. -->
|
||||
40
.github/ISSUE_TEMPLATE/Regression.md
vendored
Normal file
40
.github/ISSUE_TEMPLATE/Regression.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: "\U0001F4A5 Regression"
|
||||
about: Report an unexpected while upgrading your Nest application!
|
||||
title: ''
|
||||
labels: 'type: bug :sob:,needs triage'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Regression
|
||||
|
||||
<!-- First check out: https://docs.nestjs.com/migration-guide -->
|
||||
|
||||
## Potential Commit/PR that introduced the regression**
|
||||
<!-- If you have time to investigate, what PR/date introduced this issue. -->
|
||||
|
||||
## Describe the regression
|
||||
<!-- A clear and concise description of what the regression is. -->
|
||||
|
||||
## Input Code
|
||||
<!--- If you have link to our REPL or a standalone repo please link that! -->
|
||||
|
||||
```ts
|
||||
const your = (code) => here;
|
||||
```
|
||||
|
||||
## Expected behavior/code
|
||||
<!-- A clear and concise description of what you expected to happen (or code). -->
|
||||
|
||||
## Environment
|
||||
<pre><code>
|
||||
Nest version: A.B.C -> X.Y.Z
|
||||
|
||||
For Tooling issues:
|
||||
- Node version: XX <!-- run `node --version` -->
|
||||
- Platform: <!-- Mac, Linux, Windows -->
|
||||
|
||||
Others:
|
||||
<!-- Anything else relevant? Operating system version, IDE, package manager, ... -->
|
||||
</code></pre>
|
||||
14
.github/ISSUE_TEMPLATE/Support_question.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/Support_question.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: "\U0001F917 Support Question"
|
||||
about: "If you have a question \U0001F4AC, please check out our Discord or StackOverflow!"
|
||||
title: ''
|
||||
labels: 'type: question 🙌,needs triage'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- We primarily use GitHub as an issue tracker; for usage and support questions, please check out these resources below. Thanks! 😁. -->
|
||||
|
||||
* Discord Community Chat: https://discord.gg/G7Qnnhy
|
||||
* StackOverflow: https://stackoverflow.com/questions/tagged/nestjs using the tag `nestjs`
|
||||
* Twitter: If it's just a quick question you can ping our Twitter: https://twitter.com/nestframework
|
||||
36
.github/lock.yml
vendored
Normal file
36
.github/lock.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Number of days of inactivity before a closed issue or pull request is locked
|
||||
daysUntilLock: 90
|
||||
|
||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
||||
skipCreatedBefore: false
|
||||
|
||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
||||
exemptLabels: []
|
||||
|
||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
||||
lockLabel: false
|
||||
|
||||
# Comment to post before locking. Set to `false` to disable
|
||||
lockComment: >
|
||||
This thread has been automatically locked since there has not been
|
||||
any recent activity after it was closed. Please open a new issue for
|
||||
related bugs.
|
||||
|
||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
||||
setLockReason: true
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
|
||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - help-wanted
|
||||
# lockLabel: outdated
|
||||
|
||||
# pulls:
|
||||
# daysUntilLock: 30
|
||||
|
||||
# Repository to extend settings from
|
||||
# _extends: repo
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@ node_modules/
|
||||
/.idea
|
||||
/.awcache
|
||||
/.vscode
|
||||
*.code-workspace
|
||||
|
||||
# bundle
|
||||
packages/**/*.d.ts
|
||||
|
||||
24
.travis.yml
24
.travis.yml
@@ -1,24 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
- "10"
|
||||
- "11"
|
||||
cache:
|
||||
directories:
|
||||
- "node_modules"
|
||||
git:
|
||||
depth: 5
|
||||
addons:
|
||||
firefox: "latest"
|
||||
before_script:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
before_install:
|
||||
- npm i -g npm@latest
|
||||
install:
|
||||
- npm ci
|
||||
- npm run build:dev
|
||||
script:
|
||||
- npm run lint
|
||||
- npm test
|
||||
after_success: npm run coverage
|
||||
112
CONTRIBUTING.md
Normal file → Executable file
112
CONTRIBUTING.md
Normal file → Executable file
@@ -4,13 +4,14 @@ We would love for you to contribute to Nest and help make it even better than it
|
||||
today! As a contributor, here are the guidelines we would like you to follow:
|
||||
|
||||
<!--* [Code of Conduct](#coc)-->
|
||||
* [Question or Problem?](#question)
|
||||
* [Issues and Bugs](#issue)
|
||||
* [Feature Requests](#feature)
|
||||
* [Submission Guidelines](#submit)
|
||||
* [Development Setup](#development)
|
||||
* [Coding Rules](#rules)
|
||||
* [Commit Message Guidelines](#commit)
|
||||
|
||||
- [Question or Problem?](#question)
|
||||
- [Issues and Bugs](#issue)
|
||||
- [Feature Requests](#feature)
|
||||
- [Submission Guidelines](#submit)
|
||||
- [Development Setup](#development)
|
||||
- [Coding Rules](#rules)
|
||||
- [Commit Message Guidelines](#commit)
|
||||
<!-- - [Signing the CLA](#cla) -->
|
||||
|
||||
<!-- ## <a name="coc"></a> Code of Conduct
|
||||
@@ -24,12 +25,12 @@ Stack Overflow is a much better place to ask questions since:
|
||||
|
||||
<!-- - there are thousands of people willing to help on Stack Overflow [maybe one day] -->
|
||||
|
||||
* questions and answers stay available for public viewing so your question / answer might help someone else
|
||||
* Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||
|
||||
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
||||
|
||||
If you would like to chat about the question in real-time, you can reach out via [our gitter channel][gitter].
|
||||
If you would like to chat about the question in real-time, you can reach out via [our discord channel][discord].
|
||||
|
||||
## <a name="issue"></a> Found a Bug?
|
||||
|
||||
@@ -44,10 +45,10 @@ Repository. If you would like to _implement_ a new feature, please submit an iss
|
||||
a proposal for your work first, to be sure that we can use it.
|
||||
Please consider what kind of change it is:
|
||||
|
||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||
- For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||
and help you to craft the change so that it is successfully accepted into the project. For your issue name, please prefix your proposal with `[discussion]`, for example "[discussion]: your feature idea".
|
||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||
- **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
@@ -57,9 +58,9 @@ Before you submit an issue, please search the issue tracker, maybe an issue for
|
||||
|
||||
We want to fix all the issues as soon as possible, but before fixing a bug we need to reproduce and confirm it. In order to reproduce bugs we will systematically ask you to provide a minimal reproduction scenario using a repository or [Gist](https://gist.github.com/). Having a live, reproducible scenario gives us wealth of important information without going back & forth to you with additional questions like:
|
||||
|
||||
* version of NestJS used
|
||||
* 3rd-party libraries and their versions
|
||||
* and most importantly - a use-case that fails
|
||||
- version of NestJS used
|
||||
- 3rd-party libraries and their versions
|
||||
- and most importantly - a use-case that fails
|
||||
|
||||
<!--
|
||||
// TODO we need to create a playground, similar to plunkr
|
||||
@@ -114,11 +115,11 @@ We cannot accept code without this.
|
||||
|
||||
1. In GitHub, send a pull request to `nestjs:master`.
|
||||
|
||||
* If we suggest changes then:
|
||||
- If we suggest changes then:
|
||||
|
||||
* Make the required updates.
|
||||
* Re-run the Nest test suites to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
- Make the required updates.
|
||||
- Re-run the Nest test suites to ensure tests are still passing.
|
||||
- Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
@@ -132,25 +133,25 @@ That's it! Thank you for your contribution!
|
||||
After your pull request is merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
* Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
|
||||
- Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
|
||||
|
||||
```shell
|
||||
git push origin --delete my-fix-branch
|
||||
```
|
||||
|
||||
* Check out the master branch:
|
||||
- Check out the master branch:
|
||||
|
||||
```shell
|
||||
git checkout master -f
|
||||
```
|
||||
|
||||
* Delete the local branch:
|
||||
- Delete the local branch:
|
||||
|
||||
```shell
|
||||
git branch -D my-fix-branch
|
||||
```
|
||||
|
||||
* Update your master with the latest upstream version:
|
||||
- Update your master with the latest upstream version:
|
||||
|
||||
```shell
|
||||
git pull --ff upstream master
|
||||
@@ -177,13 +178,8 @@ That will compile fresh packages and afterward, move them to all `sample` direct
|
||||
### <a name="common-scripts"></a>Commonly used NPM scripts
|
||||
|
||||
```bash
|
||||
# build all packages and put them near to their source .ts files
|
||||
$ npm run build
|
||||
|
||||
# build all packages and move to "sample" and "integration" directories
|
||||
# if cross-packages breaking changes were performed you may face irrelevant errors
|
||||
# in order to verify the build, you can run this command again then
|
||||
$ npm run build:dev
|
||||
$ npm run build
|
||||
|
||||
# run the full unit tests suite
|
||||
$ npm run test
|
||||
@@ -194,6 +190,9 @@ $ sh scripts/run-integration.sh
|
||||
|
||||
# run linter
|
||||
$ npm run lint
|
||||
|
||||
# build all packages and put them near to their source .ts files
|
||||
$ npm run build:prod
|
||||
```
|
||||
|
||||
## <a name="rules"></a> Coding Rules
|
||||
@@ -204,10 +203,9 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
|
||||
// We're working on auto-documentation.
|
||||
* All public API methods **must be documented**. (Details TBC). -->
|
||||
|
||||
* All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
||||
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
|
||||
**100 characters**. An automated formatter is available, see
|
||||
[DEVELOPER.md](docs/DEVELOPER.md#clang-format).
|
||||
- All features or bug fixes **must be tested** by one or more specs (unit-tests).
|
||||
- We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
|
||||
**100 characters**. An automated formatter is available (`npm run format`).
|
||||
|
||||
## <a name="commit"></a> Commit Message Guidelines
|
||||
|
||||
@@ -250,15 +248,15 @@ If the commit reverts a previous commit, it should begin with `revert:`, followe
|
||||
|
||||
Must be one of the following:
|
||||
|
||||
* **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
* **docs**: Documentation only changes
|
||||
* **feature**: A new feature
|
||||
* **bugfix**: A bug fix
|
||||
* **perf**: A code change that improves performance
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
- **build**: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
- **ci**: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
|
||||
- **docs**: Documentation only changes
|
||||
- **feature**: A new feature
|
||||
- **bugfix**: A bug fix
|
||||
- **perf**: A code change that improves performance
|
||||
- **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
- **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
### Scope
|
||||
|
||||
@@ -266,28 +264,28 @@ The scope should be the name of the npm package affected (as perceived by person
|
||||
|
||||
The following is the list of supported scopes:
|
||||
|
||||
* **common**
|
||||
* **core**
|
||||
* **sample**
|
||||
* **microservices**
|
||||
* **testing**
|
||||
* **websockets**
|
||||
- **common**
|
||||
- **core**
|
||||
- **sample**
|
||||
- **microservices**
|
||||
- **testing**
|
||||
- **websockets**
|
||||
|
||||
There are currently a few exceptions to the "use package name" rule:
|
||||
|
||||
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||
* **changelog**: used for updating the release notes in CHANGELOG.md
|
||||
* **sample/#**: for the example apps directory, replacing # with the example app number
|
||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||
<!-- * **aio**: used for docs-app (angular.io) related changes within the /aio directory of the repo -->
|
||||
- **packaging**: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||
- **changelog**: used for updating the release notes in CHANGELOG.md
|
||||
- **sample/#**: for the example apps directory, replacing # with the example app number
|
||||
- none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||
<!-- * **aio**: used for docs-app (angular.io) related changes within the /aio directory of the repo -->
|
||||
|
||||
### Subject
|
||||
|
||||
The subject contains succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* don't capitalize first letter
|
||||
* no dot (.) at the end
|
||||
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- don't capitalize first letter
|
||||
- no dot (.) at the end
|
||||
|
||||
### Body
|
||||
|
||||
@@ -320,7 +318,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[dev-doc]: https://github.com/nestjs/nest/blob/master/docs/DEVELOPER.md
|
||||
[github]: https://github.com/nestjs/nest
|
||||
[gitter]: https://gitter.im/nestjs/nest
|
||||
[discord]: https://discordapp.com/invite/G7Qnnhy
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[js-style-guide]: https://google.github.io/styleguide/jsguide.html
|
||||
[jsfiddle]: http://jsfiddle.net
|
||||
|
||||
71
Readme.md
71
Readme.md
@@ -2,34 +2,30 @@
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/nestjs/nest
|
||||
[linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
|
||||
[linux-url]: https://travis-ci.org/nestjs/nest
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="blank">Node.js</a> framework for building efficient and scalable server-side applications, heavily inspired by <a href="https://angular.io" target="blank">Angular</a>.</p>
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://travis-ci.org/nestjs/nest"><img src="https://api.travis-ci.org/nestjs/nest.svg?branch=master" alt="Travis" /></a>
|
||||
<a href="https://travis-ci.org/nestjs/nest"><img src="https://img.shields.io/travis/nestjs/nest/master.svg?label=linux" alt="Linux" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#8" alt="Coverage" /></a>
|
||||
<a href="https://gitter.im/nestjs/nestjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge"><img src="https://badges.gitter.im/nestjs/nestjs.svg" alt="Gitter" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
<a href="https://opencollective.com/nest#backer"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://twitter.com/nestframework"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/core.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
</p>
|
||||
<!--[](https://opencollective.com/nest#backer)
|
||||
[](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
<p>Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).</p>
|
||||
Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
## Philosophy
|
||||
|
||||
@@ -41,35 +37,44 @@
|
||||
* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
|
||||
## 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).
|
||||
|
||||
#### Principal Sponsor
|
||||
|
||||
<a href="https://valor-software.com/"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
|
||||
#### Base Sponsor
|
||||
|
||||
<a href="https://blueanchor.io/"><img src="https://nestjs.com/img/blueanchor.png" width="300" /></a>
|
||||
<a href="https://www.novologic.com/"><img src="https://nestjs.com/img/novologic.png" width="200" /></a>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="320" /></a>
|
||||
|
||||
#### Silver Sponsors
|
||||
<a href="https://neoteric.eu/"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a>
|
||||
<a href="http://gojob.com"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" height="95" /></a> <a href="https://www.swingdev.io"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="150" valign="middle" /> </a>
|
||||
<a href="https://yakaz.com/"><img src="https://nestjs.com/img/yakaz.png" width="100" valign="middle" /></a>
|
||||
<a href="http://xtremis.com/"><img src="https://nestjs.com/img/logo-xtremis.svg" width="150" valign="middle" /></a>
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" height="95" /></a>
|
||||
<a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="150" valign="middle" /></a>
|
||||
<a href="http://www.leogistics.com" target="_blank"><img src="https://nestjs.com/img/leogistics-logo.jpeg" width="150" valign="middle" /></a>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<a href="https://scal.io"><img src="https://nestjs.com/img/scalio-logo.svg" width="110" valign="middle" /></a> <a href="http://angularity.io"><img src="http://angularity.io/media/logo.svg" height="30" valign="middle" /></a> <!--<a href="https://keycdn.com"><img src="https://nestjs.com/img/keycdn.svg" height="30" /></a> --> <a href="https://hostpresto.com"><img src="https://nestjs.com/img/hostpresto.png" height="30" valign="middle" /></a> <a href="https://genuinebee.com/"><img src="https://nestjs.com/img/genuinebee.svg" height="38" valign="middle" /></a> <a href="http://architectnow.net/"><img src="https://nestjs.com/img/architectnow.png" height="24" valign="middle" /></a> <a href="https://quander.io/"><img src="https://nestjs.com/img/quander.png" height="28" valign="middle" /></a> <a href="https://mantro.net/"><img src="https://nestjs.com/img/mantro-logo.svg" height="20" valign="middle" /></a>
|
||||
|
||||
<a href="https://triplebyte.com/"><img src="https://nestjs.com/img/triplebyte.png" height="30" valign="middle" /></a>
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a> <a href="https://blueanchor.io/" target="_blank"><img src="https://nestjs.com/img/blueanchor.png" width="150" valign="middle" /></a>
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a>
|
||||
<a href="https://scal.io" target="_blank"><img src="https://nestjs.com/img/scalio-logo.svg" width="80" valign="middle" /></a> <a href="http://angularity.io" target="_blank"><img src="http://angularity.io/media/logo.svg" height="22" valign="middle" /></a> <!--<a href="https://keycdn.com"><img src="https://nestjs.com/img/keycdn.svg" height="30" /></a> --> <a href="https://hostpresto.com" target="_blank"><img src="https://nestjs.com/img/hostpresto.png" height="24" valign="middle" /></a>
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" height="14" valign="middle" /></a>
|
||||
<a href="https://buddy.works/" target="_blank"><img src="https://nestjs.com/img/buddy-logo.svg" height="25" valign="middle" /></a>
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" height="25" valign="middle" /></a> <a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" height="24" valign="middle" /></a> <a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" height="20" valign="middle" /></a> <a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" height="22" valign="middle" /></a> <a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" height="19" valign="middle" /></a> <a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" height="20" valign="middle" /></a>
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" height="18" valign="middle" /></a> <a href="https://yakaz.com/" target="_blank"><img src="https://nestjs.com/img/yakaz.png" width="70" valign="middle" /></a>
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a>
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a>
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a>
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a>
|
||||
<a href="https://tekhattan.com" target="_blank"><img src="https://nestjs.com/img/tekhattan-logo.png" width="110" valign="middle" /></a>
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a>
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
<a href="https://opencollective.com/nest"><img src="https://opencollective.com/nest/backers.svg?width=890"></a>
|
||||
<a href="https://opencollective.com/nest" target="_blank"><img src="https://opencollective.com/nest/backers.svg?width=1000"></a>
|
||||
|
||||
## Stay in touch
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo 'Library:' $1
|
||||
node $1 &
|
||||
pid=$!
|
||||
|
||||
sleep 2
|
||||
|
||||
wrk 'http://localhost:3000' \
|
||||
-d 10 \
|
||||
-c 1024 \
|
||||
-t 8
|
||||
|
||||
kill $pid
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
: > all_output.txt
|
||||
|
||||
lib=(express fastify nest nest-fastify)
|
||||
for item in ${lib[*]}
|
||||
do
|
||||
echo '-----------------------' >> all_output.txt
|
||||
echo $item >> all_output.txt
|
||||
echo '-----------------------' >> all_output.txt
|
||||
|
||||
node $item &
|
||||
pid=$!
|
||||
|
||||
sleep 2
|
||||
|
||||
wrk 'http://localhost:3000' \
|
||||
-d 10 \
|
||||
-c 1024 \
|
||||
-t 8 >> all_output.txt
|
||||
|
||||
kill $pid
|
||||
done
|
||||
123
gulpfile.js
123
gulpfile.js
@@ -1,117 +1,16 @@
|
||||
const fs = require('fs');
|
||||
'use strict';
|
||||
/**
|
||||
* Load the TypeScript compiler, then load the TypeScript gulpfile which simply loads all
|
||||
* the tasks. The tasks are really inside tools/gulp/tasks.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const ts = require('gulp-typescript');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const clean = require('gulp-clean');
|
||||
const deleteEmpty = require('delete-empty');
|
||||
|
||||
const packages = {
|
||||
common: ts.createProject('packages/common/tsconfig.json'),
|
||||
core: ts.createProject('packages/core/tsconfig.json'),
|
||||
microservices: ts.createProject('packages/microservices/tsconfig.json'),
|
||||
websockets: ts.createProject('packages/websockets/tsconfig.json'),
|
||||
testing: ts.createProject('packages/testing/tsconfig.json'),
|
||||
'platform-express': ts.createProject(
|
||||
'packages/platform-express/tsconfig.json',
|
||||
),
|
||||
'platform-fastify': ts.createProject(
|
||||
'packages/platform-fastify/tsconfig.json',
|
||||
),
|
||||
'platform-socket.io': ts.createProject(
|
||||
'packages/platform-socket.io/tsconfig.json',
|
||||
),
|
||||
'platform-ws': ts.createProject('packages/platform-ws/tsconfig.json'),
|
||||
};
|
||||
const modules = Object.keys(packages);
|
||||
const source = 'packages';
|
||||
const distId = process.argv.indexOf('--dist');
|
||||
const dist = distId < 0 ? source : process.argv[distId + 1];
|
||||
const projectDir = __dirname;
|
||||
const tsconfigPath = path.join(projectDir, 'tools/gulp/tsconfig.json');
|
||||
|
||||
gulp.task('default', function() {
|
||||
modules.forEach(module => {
|
||||
gulp.watch(
|
||||
[`${source}/${module}/**/*.ts`, `${source}/${module}/*.ts`],
|
||||
[module],
|
||||
);
|
||||
});
|
||||
require('ts-node').register({
|
||||
project: tsconfigPath
|
||||
});
|
||||
|
||||
gulp.task('copy-misc', function() {
|
||||
return gulp
|
||||
.src(['Readme.md', 'LICENSE', '.npmignore'])
|
||||
.pipe(gulp.dest(`${source}/common`))
|
||||
.pipe(gulp.dest(`${source}/core`))
|
||||
.pipe(gulp.dest(`${source}/microservices`))
|
||||
.pipe(gulp.dest(`${source}/websockets`))
|
||||
.pipe(gulp.dest(`${source}/testing`))
|
||||
.pipe(gulp.dest(`${source}/platform-fastify`))
|
||||
.pipe(gulp.dest(`${source}/platform-express`))
|
||||
.pipe(gulp.dest(`${source}/platform-ws`))
|
||||
.pipe(gulp.dest(`${source}/platform-socket.io`));
|
||||
});
|
||||
|
||||
gulp.task('clean:output', function() {
|
||||
return gulp
|
||||
.src(
|
||||
[`${source}/**/*.js`, `${source}/**/*.d.ts`, `${source}/**/*.js.map`],
|
||||
{
|
||||
read: false,
|
||||
},
|
||||
)
|
||||
.pipe(clean());
|
||||
});
|
||||
|
||||
gulp.task('clean:dirs', function(done) {
|
||||
deleteEmpty.sync(`${source}/`);
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task('clean:bundle', gulp.series('clean:output', 'clean:dirs'));
|
||||
|
||||
modules.forEach(module => {
|
||||
gulp.task(module, () => {
|
||||
return packages[module]
|
||||
.src()
|
||||
.pipe(packages[module]())
|
||||
.pipe(gulp.dest(`${dist}/${module}`));
|
||||
});
|
||||
});
|
||||
|
||||
modules.forEach(module => {
|
||||
gulp.task(module + ':dev', () => {
|
||||
return packages[module]
|
||||
.src()
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(packages[module]())
|
||||
.pipe(
|
||||
sourcemaps.mapSources(sourcePath => './' + sourcePath.split('/').pop()),
|
||||
)
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest(`${dist}/${module}`));
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('common:dev', gulp.series(modules.map(module => module + ':dev')));
|
||||
gulp.task('build', gulp.series(modules));
|
||||
gulp.task('build:dev', gulp.series('common:dev'));
|
||||
|
||||
function getFolders(dir) {
|
||||
return fs.readdirSync(dir).filter(function(file) {
|
||||
return fs.statSync(path.join(dir, file)).isDirectory();
|
||||
});
|
||||
}
|
||||
gulp.task('move', function() {
|
||||
const getDirs = base => getFolders(base).map(path => `${base}/${path}`);
|
||||
|
||||
const examplesDirs = getDirs('sample');
|
||||
const integrationDirs = getDirs('integration');
|
||||
const directories = examplesDirs.concat(integrationDirs);
|
||||
|
||||
let stream = gulp.src(['node_modules/@nestjs/**/*']);
|
||||
|
||||
directories.forEach(dir => {
|
||||
stream = stream.pipe(gulp.dest(dir + '/node_modules/@nestjs'));
|
||||
});
|
||||
return stream;
|
||||
});
|
||||
require('./tools/gulp/gulpfile');
|
||||
@@ -23,8 +23,7 @@ services:
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
mysql:
|
||||
image: mysql:5.7.25
|
||||
restart: always
|
||||
image: mysql:5.7.27
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: test
|
||||
@@ -32,15 +31,42 @@ services:
|
||||
- "3306:3306"
|
||||
restart: always
|
||||
mongodb:
|
||||
container_name: test-mongodb
|
||||
image: mongo:latest
|
||||
environment:
|
||||
- MONGODB_DATABASE="test"
|
||||
ports:
|
||||
- 27017:27017
|
||||
rabbit:
|
||||
container_name: test-rabbit
|
||||
hostname: rabbit
|
||||
image: "rabbitmq:management"
|
||||
ports:
|
||||
- "15672:15672"
|
||||
- "5672:5672"
|
||||
tty: true
|
||||
tty: true
|
||||
zookeeper:
|
||||
container_name: test-zookeeper
|
||||
hostname: zookeeper
|
||||
image: confluentinc/cp-zookeeper:5.3.1
|
||||
ports:
|
||||
- "2181:2181"
|
||||
environment:
|
||||
ZOOKEEPER_CLIENT_PORT: 2181
|
||||
ZOOKEEPER_TICK_TIME: 2000
|
||||
kafka:
|
||||
container_name: test-kafka
|
||||
hostname: kafka
|
||||
image: confluentinc/cp-kafka:5.3.1
|
||||
depends_on:
|
||||
- zookeeper
|
||||
ports:
|
||||
- "29092:29092"
|
||||
- "9092:9092"
|
||||
environment:
|
||||
KAFKA_BROKER_ID: 1
|
||||
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
||||
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
|
||||
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
|
||||
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
||||
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
|
||||
|
||||
3223
integration/graphql/package-lock.json
generated
3223
integration/graphql/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,21 +9,21 @@
|
||||
"start:prod": "node dist/main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "6.0.1",
|
||||
"@nestjs/core": "6.0.1",
|
||||
"@nestjs/graphql": "6.0.1",
|
||||
"apollo-server-express": "2.4.8",
|
||||
"graphql": "14.1.1",
|
||||
"graphql-tools": "4.0.4",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/graphql": "6.5.3",
|
||||
"apollo-server-express": "2.9.4",
|
||||
"graphql": "14.5.8",
|
||||
"graphql-tools": "4.0.5",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"rxjs": "6.5.3",
|
||||
"subscriptions-transport-ws": "0.9.16",
|
||||
"typescript": "3.3.3333",
|
||||
"ws": "4.1.0"
|
||||
"typescript": "3.6.3",
|
||||
"ws": "7.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"ts-node": "8.0.3",
|
||||
"tslint": "5.14.0"
|
||||
"@types/node": "7.10.7",
|
||||
"ts-node": "8.4.1",
|
||||
"tslint": "5.20.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"target": "es6",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"outDir": "./dist"
|
||||
"outDir": "./dist",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
|
||||
@@ -28,6 +28,34 @@ export class TransformInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class StatusInterceptor {
|
||||
constructor(private statusCode: number) {}
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler) {
|
||||
const ctx = context.switchToHttp();
|
||||
const res = ctx.getResponse();
|
||||
res.status(this.statusCode);
|
||||
return next.handle().pipe(map(data => ({ data })));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class HeaderInterceptor {
|
||||
constructor(private headers: object) {}
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler) {
|
||||
const ctx = context.switchToHttp();
|
||||
const res = ctx.getResponse();
|
||||
for (const key in this.headers) {
|
||||
if (this.headers.hasOwnProperty(key)) {
|
||||
res.header(key, this.headers[key]);
|
||||
}
|
||||
}
|
||||
return next.handle().pipe(map(data => ({ data })));
|
||||
}
|
||||
}
|
||||
|
||||
function createTestModule(interceptor) {
|
||||
return Test.createTestingModule({
|
||||
imports: [ApplicationModule],
|
||||
@@ -87,6 +115,33 @@ describe('Interceptors', () => {
|
||||
.expect(200, { data: 'Hello world!' });
|
||||
});
|
||||
|
||||
it(`should modify response status`, async () => {
|
||||
app = (await createTestModule(
|
||||
new StatusInterceptor(400),
|
||||
)).createNestApplication();
|
||||
|
||||
await app.init();
|
||||
return request(app.getHttpServer())
|
||||
.get('/hello')
|
||||
.expect(400, { data: 'Hello world!' });
|
||||
});
|
||||
|
||||
it(`should modify Authorization header`, async () => {
|
||||
const customHeaders = {
|
||||
Authorization: 'jwt',
|
||||
};
|
||||
|
||||
app = (await createTestModule(
|
||||
new HeaderInterceptor(customHeaders),
|
||||
)).createNestApplication();
|
||||
|
||||
await app.init();
|
||||
return request(app.getHttpServer())
|
||||
.get('/hello')
|
||||
.expect(200)
|
||||
.expect('Authorization', 'jwt');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
59
integration/hello-world/e2e/middleware-execute-order.spec.ts
Normal file
59
integration/hello-world/e2e/middleware-execute-order.spec.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { INestApplication, MiddlewareConsumer, Module } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
|
||||
const RETURN_VALUE_A = 'test_A';
|
||||
const RETURN_VALUE_B = 'test_B';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
})
|
||||
class ModuleA {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => {
|
||||
res.send(RETURN_VALUE_A);
|
||||
})
|
||||
.forRoutes('hello');
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
imports: [ModuleA],
|
||||
})
|
||||
class ModuleB {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => {
|
||||
res.send(RETURN_VALUE_B);
|
||||
})
|
||||
.forRoutes('hello');
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
imports: [ModuleB],
|
||||
})
|
||||
class TestModule {}
|
||||
|
||||
describe('Middleware (execution order)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
app = (await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()).createNestApplication();
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`should execute middleware in topological order`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/hello')
|
||||
.expect(200, RETURN_VALUE_B);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
1388
integration/hello-world/package-lock.json
generated
1388
integration/hello-world/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,21 +7,21 @@
|
||||
"start": "ts-node src/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "5.7.4",
|
||||
"@nestjs/core": "5.7.4",
|
||||
"@nestjs/microservices": "5.7.4",
|
||||
"@nestjs/testing": "5.7.4",
|
||||
"@nestjs/websockets": "5.7.4",
|
||||
"class-transformer": "0.2.0",
|
||||
"class-validator": "0.9.1",
|
||||
"fastify": "2.1.0",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/microservices": "6.8.0",
|
||||
"@nestjs/testing": "6.8.0",
|
||||
"@nestjs/websockets": "6.8.0",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.10.1",
|
||||
"fastify": "2.9.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"typescript": "2.9.2"
|
||||
"rxjs": "6.5.3",
|
||||
"typescript": "3.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"@types/node": "7.10.7",
|
||||
"supertest": "4.0.2",
|
||||
"ts-node": "8.0.3"
|
||||
"ts-node": "8.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HelloService } from './hello.service';
|
||||
import { Controller, Get, Header, Param } from '@nestjs/common';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HelloService } from './hello.service';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
|
||||
@Controller('hello')
|
||||
|
||||
55
integration/hooks/e2e/before-app-shutdown.spec.ts
Normal file
55
integration/hooks/e2e/before-app-shutdown.spec.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Injectable, BeforeApplicationShutdown } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements BeforeApplicationShutdown {
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
}
|
||||
|
||||
describe('BeforeApplicationShutdown', () => {
|
||||
it('should call `beforeApplicationShutdown` when application closes', async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [TestInjectable],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.close();
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.beforeApplicationShutdown.called).to.be.true;
|
||||
});
|
||||
|
||||
it('should not stop the server once beforeApplicationShutdown has been called', async () => {
|
||||
let resolve;
|
||||
const promise = new Promise(r => (resolve = r));
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: 'Test',
|
||||
useValue: {
|
||||
beforeApplicationShutdown: () => promise,
|
||||
},
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
Sinon.stub(module, 'dispose' as any);
|
||||
const app = module.createNestApplication();
|
||||
|
||||
app.close();
|
||||
|
||||
expect(((module as any).dispose as Sinon.SinonSpy).called, 'dispose')
|
||||
.to.be.false;
|
||||
|
||||
resolve();
|
||||
|
||||
setTimeout(
|
||||
() =>
|
||||
expect(
|
||||
((module as any).dispose as Sinon.SinonSpy).called,
|
||||
'dispose',
|
||||
).to.be.true,
|
||||
0,
|
||||
);
|
||||
});
|
||||
});
|
||||
54
integration/hooks/e2e/enable-shutdown-hook.spec.ts
Normal file
54
integration/hooks/e2e/enable-shutdown-hook.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { expect } from 'chai';
|
||||
import { spawnSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('enableShutdownHooks', () => {
|
||||
it('should call the correct hooks if any shutdown signal gets invoked', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGHUP',
|
||||
]);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).to.equal('beforeApplicationShutdown SIGHUP');
|
||||
expect(calls[1]).to.equal('onApplicationShutdown SIGHUP');
|
||||
done();
|
||||
}).timeout(5000);
|
||||
|
||||
it('should call the correct hooks if a specific shutdown signal gets invoked', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'SIGINT',
|
||||
]);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).to.equal('beforeApplicationShutdown SIGINT');
|
||||
expect(calls[1]).to.equal('onApplicationShutdown SIGINT');
|
||||
done();
|
||||
}).timeout(5000);
|
||||
|
||||
it('should ignore system signals which are not specified', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'SIGHUP',
|
||||
]);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
}).timeout(5000);
|
||||
|
||||
it('should ignore system signals if "enableShutdownHooks" was not called', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'NONE',
|
||||
]);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
}).timeout(5000);
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
import {
|
||||
Injectable,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
OnApplicationShutdown,
|
||||
OnModuleDestroy,
|
||||
OnModuleInit,
|
||||
BeforeApplicationShutdown,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
@@ -15,8 +15,10 @@ class TestInjectable
|
||||
OnApplicationBootstrap,
|
||||
OnModuleInit,
|
||||
OnModuleDestroy,
|
||||
OnApplicationShutdown {
|
||||
OnApplicationShutdown,
|
||||
BeforeApplicationShutdown {
|
||||
onApplicationBootstrap = Sinon.spy();
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
onApplicationShutdown = Sinon.spy();
|
||||
onModuleDestroy = Sinon.spy();
|
||||
onModuleInit = Sinon.spy();
|
||||
@@ -37,6 +39,7 @@ describe('Lifecycle Hook Order', () => {
|
||||
instance.onModuleInit,
|
||||
instance.onApplicationBootstrap,
|
||||
instance.onModuleDestroy,
|
||||
instance.beforeApplicationShutdown,
|
||||
instance.onApplicationShutdown,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -19,4 +19,26 @@ describe('OnApplicationBootstrap', () => {
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onApplicationBootstrap.called).to.be.true;
|
||||
});
|
||||
|
||||
it('should not throw an error when onApplicationBootstrap is null', async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'TEST', useValue: { onApplicationBootstrap: null } }
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then((obj) => expect(obj).to.not.be.undefined);
|
||||
});
|
||||
|
||||
it('should not throw an error when onApplicationBootstrap is undefined', async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'TEST', useValue: { onApplicationBootstrap: undefined } }
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then((obj) => expect(obj).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
import { Injectable, OnApplicationShutdown, ShutdownSignal } from '@nestjs/common';
|
||||
import { spawn, spawnSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements OnApplicationShutdown {
|
||||
@@ -21,28 +19,4 @@ describe('OnApplicationShutdown', () => {
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onApplicationShutdown.called).to.be.true;
|
||||
});
|
||||
|
||||
it('should call onApplicationShutdown if any shutdown signal gets invoked', done => {
|
||||
const result = spawnSync('ts-node', [join(__dirname, '../src/main.ts'), 'SIGHUP']);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('Signal SIGHUP');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should call onApplicationShutdown if a specific shutdown signal gets invoked', done => {
|
||||
const result = spawnSync('ts-node', [join(__dirname, '../src/main.ts'), 'SIGINT', 'SIGINT']);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('Signal SIGINT');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should ignore system signals which are not specified', done => {
|
||||
const result = spawnSync('ts-node', [join(__dirname, '../src/main.ts'), 'SIGINT', 'SIGHUP']);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should ignore system signals if "enableShutdownHooks" was not called', done => {
|
||||
const result = spawnSync('ts-node', [join(__dirname, '../src/main.ts'), 'SIGINT', 'NONE']);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,4 +19,26 @@ describe('OnModuleDestroy', () => {
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onModuleDestroy.called).to.be.true;
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleDestroy is null', async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'TEST', useValue: { onModuleDestroy: null } }
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then((obj) => expect(obj).to.not.be.undefined);
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleDestroy is undefined', async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'TEST', useValue: { onModuleDestroy: undefined } }
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then((obj) => expect(obj).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,4 +19,26 @@ describe('OnModuleInit', () => {
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onModuleInit.called).to.be.true;
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleInit is null', async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'TEST', useValue: { onModuleInit: null } }
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then((obj) => expect(obj).to.not.be.undefined);
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleInit is undefined', async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'TEST', useValue: { onModuleInit: undefined } }
|
||||
],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then((obj) => expect(obj).to.not.be.undefined);
|
||||
});
|
||||
});
|
||||
|
||||
920
integration/hooks/package-lock.json
generated
920
integration/hooks/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,17 +7,17 @@
|
||||
"start": "ts-node src/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "5.7.4",
|
||||
"@nestjs/core": "5.7.4",
|
||||
"class-transformer": "0.2.0",
|
||||
"class-validator": "0.9.1",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.10.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"typescript": "3.3.3333"
|
||||
"rxjs": "6.5.3",
|
||||
"typescript": "3.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"@types/node": "7.10.7",
|
||||
"supertest": "4.0.2",
|
||||
"ts-node": "8.0.3"
|
||||
"ts-node": "8.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Injectable, OnApplicationShutdown, Module } from '@nestjs/common';
|
||||
import { Injectable, OnApplicationShutdown, BeforeApplicationShutdown, Module } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
const SIGNAL = process.argv[2];
|
||||
const SIGNAL_TO_LISTEN = process.argv[3];
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements OnApplicationShutdown {
|
||||
class TestInjectable implements OnApplicationShutdown, BeforeApplicationShutdown {
|
||||
beforeApplicationShutdown(signal: string) {
|
||||
console.log('beforeApplicationShutdown ' + signal);
|
||||
}
|
||||
|
||||
onApplicationShutdown(signal: string) {
|
||||
console.log('Signal ' + signal);
|
||||
console.log('onApplicationShutdown ' + signal);
|
||||
}
|
||||
}
|
||||
|
||||
37
integration/injector/e2e/core-injectables.spec.ts
Normal file
37
integration/injector/e2e/core-injectables.spec.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { CoreInjectablesModule } from '../src/core-injectables/core-injectables.module';
|
||||
import { ApplicationConfig, ModuleRef } from '@nestjs/core';
|
||||
|
||||
describe('Core Injectables', () => {
|
||||
let testingModule: TestingModule;
|
||||
|
||||
beforeEach(async () => {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [CoreInjectablesModule],
|
||||
});
|
||||
testingModule = await builder.compile();
|
||||
});
|
||||
|
||||
it('should provide ApplicationConfig as core injectable', () => {
|
||||
const applicationConfig = testingModule.get<ApplicationConfig>(
|
||||
ApplicationConfig,
|
||||
);
|
||||
|
||||
applicationConfig.setGlobalPrefix('/api');
|
||||
|
||||
expect(applicationConfig).to.not.be.undefined;
|
||||
expect(applicationConfig.getGlobalPrefix()).to.be.eq('/api');
|
||||
});
|
||||
|
||||
it('should provide ModuleRef as core injectable', () => {
|
||||
const moduleRef = testingModule.get<ModuleRef>(ModuleRef);
|
||||
expect(moduleRef).to.not.be.undefined;
|
||||
});
|
||||
|
||||
it('should provide the current Module as provider', () => {
|
||||
const module = testingModule.get<CoreInjectablesModule>(CoreInjectablesModule);
|
||||
expect(module).to.not.be.undefined;
|
||||
expect(module.constructor.name).to.be.eq('CoreInjectablesModule');
|
||||
});
|
||||
});
|
||||
54
integration/injector/e2e/scoped-instances.spec.ts
Normal file
54
integration/injector/e2e/scoped-instances.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { InvalidClassScopeException } from '@nestjs/core/errors/exceptions/invalid-class-scope.exception';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { ScopedController } from '../src/scoped/scoped.controller';
|
||||
import { ScopedModule } from '../src/scoped/scoped.module';
|
||||
import { ScopedService } from '../src/scoped/scoped.service';
|
||||
import { TransientService } from '../src/scoped/transient.service';
|
||||
|
||||
describe('Scoped Instances', () => {
|
||||
let testingModule: TestingModule;
|
||||
|
||||
beforeEach(async () => {
|
||||
testingModule = await Test.createTestingModule({
|
||||
imports: [ScopedModule],
|
||||
}).compile();
|
||||
});
|
||||
|
||||
it('should dynamically resolve transient provider', async () => {
|
||||
const transient1 = await testingModule.resolve(TransientService);
|
||||
const transient2 = await testingModule.resolve(TransientService);
|
||||
|
||||
expect(transient1).to.be.instanceOf(TransientService);
|
||||
expect(transient2).to.be.instanceOf(TransientService);
|
||||
expect(transient1).to.be.equal(transient2);
|
||||
});
|
||||
|
||||
it('should dynamically resolve request-scoped provider', async () => {
|
||||
const request1 = await testingModule.resolve(ScopedService);
|
||||
const request2 = await testingModule.resolve(ScopedService);
|
||||
const request3 = await testingModule.resolve(ScopedService, { id: 1 });
|
||||
|
||||
expect(request1).to.be.instanceOf(ScopedService);
|
||||
expect(request2).to.be.instanceOf(ScopedService);
|
||||
expect(request3).to.not.be.equal(request2);
|
||||
});
|
||||
|
||||
it('should dynamically resolve request-scoped controller', async () => {
|
||||
const request1 = await testingModule.resolve(ScopedController);
|
||||
const request2 = await testingModule.resolve(ScopedController);
|
||||
const request3 = await testingModule.resolve(ScopedController, { id: 1 });
|
||||
|
||||
expect(request1).to.be.instanceOf(ScopedController);
|
||||
expect(request2).to.be.instanceOf(ScopedController);
|
||||
expect(request3).to.not.be.equal(request2);
|
||||
});
|
||||
|
||||
it('should throw an exception when "get()" method is used', async () => {
|
||||
try {
|
||||
testingModule.get(ScopedController);
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(InvalidClassScopeException);
|
||||
}
|
||||
});
|
||||
});
|
||||
1245
integration/injector/package-lock.json
generated
1245
integration/injector/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,20 +7,20 @@
|
||||
"start": "ts-node src/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "5.7.4",
|
||||
"@nestjs/core": "5.7.4",
|
||||
"@nestjs/microservices": "5.7.4",
|
||||
"@nestjs/testing": "5.7.4",
|
||||
"@nestjs/websockets": "5.7.4",
|
||||
"class-transformer": "0.2.0",
|
||||
"class-validator": "0.9.1",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/microservices": "6.8.0",
|
||||
"@nestjs/testing": "6.8.0",
|
||||
"@nestjs/websockets": "6.8.0",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.10.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"typescript": "3.3.3333"
|
||||
"rxjs": "6.5.3",
|
||||
"typescript": "3.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"@types/node": "7.10.7",
|
||||
"supertest": "4.0.2",
|
||||
"ts-node": "8.0.3"
|
||||
"ts-node": "8.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
@Module({})
|
||||
export class CoreInjectablesModule {}
|
||||
7
integration/injector/src/scoped/scoped.controller.ts
Normal file
7
integration/injector/src/scoped/scoped.controller.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Controller, Scope } from '@nestjs/common';
|
||||
|
||||
@Controller({
|
||||
path: 'test',
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class ScopedController {}
|
||||
10
integration/injector/src/scoped/scoped.module.ts
Normal file
10
integration/injector/src/scoped/scoped.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ScopedController } from './scoped.controller';
|
||||
import { ScopedService } from './scoped.service';
|
||||
import { TransientService } from './transient.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ScopedController],
|
||||
providers: [ScopedService, TransientService],
|
||||
})
|
||||
export class ScopedModule {}
|
||||
4
integration/injector/src/scoped/scoped.service.ts
Normal file
4
integration/injector/src/scoped/scoped.service.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class ScopedService {}
|
||||
4
integration/injector/src/scoped/transient.service.ts
Normal file
4
integration/injector/src/scoped/transient.service.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class TransientService {}
|
||||
@@ -18,9 +18,15 @@ describe('MQTT transport', () => {
|
||||
|
||||
app.connectMicroservice({
|
||||
transport: Transport.MQTT,
|
||||
options: {
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
});
|
||||
app.connectMicroservice({
|
||||
transport: Transport.MQTT,
|
||||
options: {
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
|
||||
@@ -18,9 +18,15 @@ describe('NATS transport', () => {
|
||||
|
||||
app.connectMicroservice({
|
||||
transport: Transport.NATS,
|
||||
options: {
|
||||
url: 'nats://0.0.0.0:4222',
|
||||
},
|
||||
});
|
||||
app.connectMicroservice({
|
||||
transport: Transport.NATS,
|
||||
options: {
|
||||
url: 'nats://0.0.0.0:4222',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
|
||||
@@ -18,9 +18,15 @@ describe('REDIS transport', () => {
|
||||
|
||||
app.connectMicroservice({
|
||||
transport: Transport.REDIS,
|
||||
options: {
|
||||
url: 'redis://0.0.0.0:6379',
|
||||
},
|
||||
});
|
||||
app.connectMicroservice({
|
||||
transport: Transport.REDIS,
|
||||
options: {
|
||||
url: 'redis://0.0.0.0:6379',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
|
||||
169
integration/microservices/e2e/orders-grpc.spec.ts
Normal file
169
integration/microservices/e2e/orders-grpc.spec.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import * as ProtoLoader from '@grpc/proto-loader';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Transport } from '@nestjs/microservices';
|
||||
import { ExpressAdapter } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { fail } from 'assert';
|
||||
import { expect } from 'chai';
|
||||
import * as express from 'express';
|
||||
import * as GRPC from 'grpc';
|
||||
import { join } from 'path';
|
||||
import * as request from 'supertest';
|
||||
import { AdvancedGrpcController } from '../src/grpc-advanced/advanced.grpc.controller';
|
||||
|
||||
describe('Advanced GRPC transport', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
let client: any;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [AdvancedGrpcController],
|
||||
}).compile();
|
||||
// Create gRPC + HTTP server
|
||||
server = express();
|
||||
app = module.createNestApplication(new ExpressAdapter(server));
|
||||
/*
|
||||
* Create microservice configuration
|
||||
*/
|
||||
app.connectMicroservice({
|
||||
transport: Transport.GRPC,
|
||||
options: {
|
||||
url: 'localhost:5001',
|
||||
package: 'proto_example',
|
||||
protoPath: 'root.proto',
|
||||
loader: {
|
||||
includeDirs: [join(__dirname, '../src/grpc-advanced/proto')],
|
||||
keepCase: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
// Start gRPC microservice
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
// Load proto-buffers for test gRPC dispatch
|
||||
const proto = ProtoLoader.loadSync('root.proto', {
|
||||
includeDirs: [join(__dirname, '../src/grpc-advanced/proto')],
|
||||
}) as any;
|
||||
// Create Raw gRPC client object
|
||||
const protoGRPC = GRPC.loadPackageDefinition(proto) as any;
|
||||
// Create client connected to started services at standard 5000 port
|
||||
client = new protoGRPC.proto_example.orders.OrderService(
|
||||
'localhost:5001',
|
||||
GRPC.credentials.createInsecure(),
|
||||
);
|
||||
});
|
||||
|
||||
it(`GRPC Sending and Receiving HTTP POST`, () => {
|
||||
return request(server)
|
||||
.post('/')
|
||||
.send('1')
|
||||
.expect(200, {
|
||||
id: 1,
|
||||
itemTypes: [1],
|
||||
shipmentType: {
|
||||
from: 'test',
|
||||
to: 'test1',
|
||||
carrier: 'test-carrier',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('GRPC Sending and receiving message', async () => {
|
||||
// Execute find in Promise
|
||||
return new Promise(resolve => {
|
||||
client.find(
|
||||
{
|
||||
id: 1,
|
||||
},
|
||||
(err, result) => {
|
||||
// Compare results
|
||||
expect(err).to.be.null;
|
||||
expect(result).to.eql({
|
||||
id: 1,
|
||||
itemTypes: [1],
|
||||
shipmentType: {
|
||||
from: 'test',
|
||||
to: 'test1',
|
||||
carrier: 'test-carrier',
|
||||
},
|
||||
});
|
||||
// Resolve after checkups
|
||||
resolve();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('GRPC Sending and receiving Stream from RX handler', async () => {
|
||||
const callHandler = client.sync();
|
||||
|
||||
callHandler.on('data', (msg: number) => {
|
||||
// Do deep comparison (to.eql)
|
||||
expect(msg).to.eql({
|
||||
id: 1,
|
||||
itemTypes: [1],
|
||||
shipmentType: {
|
||||
from: 'test',
|
||||
to: 'test1',
|
||||
carrier: 'test-carrier',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
callHandler.on('error', (err: any) => {
|
||||
// We want to fail only on real errors while Cancellation error
|
||||
// is expected
|
||||
if (
|
||||
String(err)
|
||||
.toLowerCase()
|
||||
.indexOf('cancelled') === -1
|
||||
) {
|
||||
fail('gRPC Stream error happened, error: ' + err);
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
callHandler.write({
|
||||
id: 1,
|
||||
});
|
||||
setTimeout(() => resolve(), 1000);
|
||||
});
|
||||
});
|
||||
|
||||
it('GRPC Sending and receiving Stream from Call handler', async () => {
|
||||
const callHandler = client.syncCall();
|
||||
|
||||
callHandler.on('data', (msg: number) => {
|
||||
// Do deep comparison (to.eql)
|
||||
expect(msg).to.eql({
|
||||
id: 1,
|
||||
itemTypes: [1],
|
||||
shipmentType: {
|
||||
from: 'test',
|
||||
to: 'test1',
|
||||
carrier: 'test-carrier',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
callHandler.on('error', (err: any) => {
|
||||
// We want to fail only on real errors while Cancellation error
|
||||
// is expected
|
||||
if (
|
||||
String(err)
|
||||
.toLowerCase()
|
||||
.indexOf('cancelled') === -1
|
||||
) {
|
||||
fail('gRPC Stream error happened, error: ' + err);
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
callHandler.write({
|
||||
id: 1,
|
||||
});
|
||||
setTimeout(() => resolve(), 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as ProtoLoader from '@grpc/proto-loader';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Transport } from '@nestjs/microservices';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { fail } from 'assert';
|
||||
import { expect } from 'chai';
|
||||
import * as GRPC from 'grpc';
|
||||
import { join } from 'path';
|
||||
import * as request from 'supertest';
|
||||
import { GrpcController } from '../src/grpc/grpc.controller';
|
||||
@@ -8,8 +12,9 @@ import { GrpcController } from '../src/grpc/grpc.controller';
|
||||
describe('GRPC transport', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
let client: any;
|
||||
|
||||
beforeEach(async () => {
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [GrpcController],
|
||||
}).compile();
|
||||
@@ -24,18 +29,83 @@ describe('GRPC transport', () => {
|
||||
protoPath: join(__dirname, '../src/grpc/math.proto'),
|
||||
},
|
||||
});
|
||||
// Start gRPC microservice
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
// Load proto-buffers for test gRPC dispatch
|
||||
const proto = ProtoLoader.loadSync(
|
||||
join(__dirname, '../src/grpc/math.proto'),
|
||||
) as any;
|
||||
// Create Raw gRPC client object
|
||||
const protoGRPC = GRPC.loadPackageDefinition(proto) as any;
|
||||
// Create client connected to started services at standard 5000 port
|
||||
client = new protoGRPC.math.Math(
|
||||
'localhost:5000',
|
||||
GRPC.credentials.createInsecure(),
|
||||
);
|
||||
});
|
||||
|
||||
it(`/POST`, () => {
|
||||
it(`GRPC Sending and Receiving HTTP POST`, () => {
|
||||
return request(server)
|
||||
.post('/')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200, { result: 15 });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
it('GRPC Sending and receiving Stream from RX handler', async () => {
|
||||
const callHandler = client.SumStream();
|
||||
|
||||
callHandler.on('data', (msg: number) => {
|
||||
expect(msg).to.eql({ result: 15 });
|
||||
callHandler.cancel();
|
||||
});
|
||||
|
||||
callHandler.on('error', (err: any) => {
|
||||
// We want to fail only on real errors while Cancellation error
|
||||
// is expected
|
||||
if (
|
||||
String(err)
|
||||
.toLowerCase()
|
||||
.indexOf('cancelled') === -1
|
||||
) {
|
||||
fail('gRPC Stream error happened, error: ' + err);
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
callHandler.write({ data: [1, 2, 3, 4, 5] });
|
||||
setTimeout(() => resolve(), 1000);
|
||||
});
|
||||
});
|
||||
|
||||
it('GRPC Sending and receiving Stream from Call Passthrough handler', async () => {
|
||||
const callHandler = client.SumStreamPass();
|
||||
|
||||
callHandler.on('data', (msg: number) => {
|
||||
expect(msg).to.eql({ result: 15 });
|
||||
callHandler.cancel();
|
||||
});
|
||||
|
||||
callHandler.on('error', (err: any) => {
|
||||
// We want to fail only on real errors while Cancellation error
|
||||
// is expected
|
||||
if (
|
||||
String(err)
|
||||
.toLowerCase()
|
||||
.indexOf('cancelled') === -1
|
||||
) {
|
||||
fail('gRPC Stream error happened, error: ' + err);
|
||||
}
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
callHandler.write({ data: [1, 2, 3, 4, 5] });
|
||||
setTimeout(() => resolve(), 1000);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
client.close();
|
||||
});
|
||||
});
|
||||
|
||||
140
integration/microservices/e2e/sum-kafka.spec.ts
Normal file
140
integration/microservices/e2e/sum-kafka.spec.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Transport } from '@nestjs/microservices';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { BusinessDto } from '../src/kafka/dtos/business.dto';
|
||||
import { UserDto } from '../src/kafka/dtos/user.dto';
|
||||
import { UserEntity } from '../src/kafka/entities/user.entity';
|
||||
import { KafkaController } from '../src/kafka/kafka.controller';
|
||||
import { KafkaMessagesController } from '../src/kafka/kafka.messages.controller';
|
||||
|
||||
describe('Kafka transport', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
it(`Start Kafka app`, async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [KafkaController, KafkaMessagesController],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpAdapter().getInstance();
|
||||
|
||||
app.connectMicroservice({
|
||||
transport: Transport.KAFKA,
|
||||
options: {
|
||||
client: {
|
||||
brokers: ['localhost:9092'],
|
||||
},
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
}).timeout(30000);
|
||||
|
||||
it(`/POST (sync sum kafka message)`, () => {
|
||||
return request(server)
|
||||
.post('/mathSumSyncKafkaMessage')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200)
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (sync sum kafka(ish) message without key and only the value)`, () => {
|
||||
return request(server)
|
||||
.post('/mathSumSyncWithoutKey')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200)
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (sync sum plain object)`, () => {
|
||||
return request(server)
|
||||
.post('/mathSumSyncPlainObject')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200)
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (sync sum array)`, () => {
|
||||
return request(server)
|
||||
.post('/mathSumSyncArray')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200)
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (sync sum string)`, () => {
|
||||
return request(server)
|
||||
.post('/mathSumSyncString')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.expect(200)
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (sync sum number)`, () => {
|
||||
return request(server)
|
||||
.post('/mathSumSyncNumber')
|
||||
.send([12345])
|
||||
.expect(200)
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (async event notification)`, done => {
|
||||
request(server)
|
||||
.post('/notify')
|
||||
.send()
|
||||
.end(() => {
|
||||
setTimeout(() => {
|
||||
expect(KafkaController.IS_NOTIFIED).to.be.true;
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
const userDto: UserDto = {
|
||||
email: 'enriquebenavidesm@gmail.com',
|
||||
name: 'Ben',
|
||||
phone: '1112223331',
|
||||
years: 33,
|
||||
};
|
||||
const newUser: UserEntity = new UserEntity(userDto);
|
||||
const businessDto: BusinessDto = {
|
||||
name: 'Example',
|
||||
phone: '2233441122',
|
||||
user: newUser,
|
||||
};
|
||||
it(`/POST (sync command create user)`, () => {
|
||||
return request(server)
|
||||
.post('/user')
|
||||
.send(userDto)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`/POST (sync command create business`, () => {
|
||||
return request(server)
|
||||
.post('/business')
|
||||
.send(businessDto)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it(`/POST (sync command create user) Concurrency Test`, async () => {
|
||||
const promises = [];
|
||||
for (let concurrencyKey = 0; concurrencyKey < 100; concurrencyKey++) {
|
||||
const innerUserDto = JSON.parse(JSON.stringify(userDto));
|
||||
innerUserDto.name += `+${concurrencyKey}`;
|
||||
promises.push(
|
||||
request(server)
|
||||
.post('/user')
|
||||
.send(userDto)
|
||||
.expect(200),
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
});
|
||||
|
||||
after(`Stopping Kafka app`, async () => {
|
||||
await app.close();
|
||||
});
|
||||
}).timeout(30000);
|
||||
@@ -19,6 +19,9 @@ describe('MQTT transport', () => {
|
||||
|
||||
app.connectMicroservice({
|
||||
transport: Transport.MQTT,
|
||||
options: {
|
||||
url: 'mqtt://0.0.0.0:1883',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
@@ -46,7 +49,7 @@ describe('MQTT transport', () => {
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (concurrent)`, () => {
|
||||
it(`/POST (concurrent)`, function() {
|
||||
return request(server)
|
||||
.post('/concurrent')
|
||||
.send([
|
||||
@@ -62,7 +65,7 @@ describe('MQTT transport', () => {
|
||||
Array.from({ length: 10 }, (v, k) => k + 91),
|
||||
])
|
||||
.expect(200, 'true');
|
||||
});
|
||||
}).timeout(5000);
|
||||
|
||||
it(`/POST (streaming)`, () => {
|
||||
return request(server)
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('NATS transport', () => {
|
||||
app.connectMicroservice({
|
||||
transport: Transport.NATS,
|
||||
options: {
|
||||
url: 'nats://localhost:4222',
|
||||
url: 'nats://0.0.0.0:4222',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
|
||||
@@ -19,6 +19,9 @@ describe('REDIS transport', () => {
|
||||
|
||||
app.connectMicroservice({
|
||||
transport: Transport.REDIS,
|
||||
options: {
|
||||
url: 'redis://0.0.0.0:6379',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
@@ -46,7 +49,9 @@ describe('REDIS transport', () => {
|
||||
.expect(200, '15');
|
||||
});
|
||||
|
||||
it(`/POST (concurrent)`, () => {
|
||||
it(`/POST (concurrent)`, function() {
|
||||
this.retries(10);
|
||||
|
||||
return request(server)
|
||||
.post('/concurrent')
|
||||
.send([
|
||||
@@ -62,7 +67,7 @@ describe('REDIS transport', () => {
|
||||
Array.from({ length: 10 }, (v, k) => k + 91),
|
||||
])
|
||||
.expect(200, 'true');
|
||||
});
|
||||
}).timeout(5000);
|
||||
|
||||
it(`/POST (streaming)`, () => {
|
||||
return request(server)
|
||||
|
||||
@@ -20,9 +20,10 @@ describe('RabbitMQ transport', () => {
|
||||
app.connectMicroservice({
|
||||
transport: Transport.RMQ,
|
||||
options: {
|
||||
urls: [`amqp://localhost:5672`],
|
||||
urls: [`amqp://0.0.0.0:5672`],
|
||||
queue: 'test',
|
||||
queueOptions: { durable: false },
|
||||
socketOptions: { noDelay: true },
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Transport } from '@nestjs/microservices';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { AppController } from '../src/app.controller';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
|
||||
describe('RPC transport', () => {
|
||||
@@ -18,6 +20,9 @@ describe('RPC transport', () => {
|
||||
|
||||
app.connectMicroservice({
|
||||
transport: Transport.TCP,
|
||||
options: {
|
||||
host: '0.0.0.0',
|
||||
},
|
||||
});
|
||||
await app.startAllMicroservicesAsync();
|
||||
await app.init();
|
||||
@@ -76,6 +81,18 @@ describe('RPC transport', () => {
|
||||
.expect(500);
|
||||
});
|
||||
|
||||
it(`/POST (event notification)`, done => {
|
||||
request(server)
|
||||
.post('/notify')
|
||||
.send([1, 2, 3, 4, 5])
|
||||
.end(() => {
|
||||
setTimeout(() => {
|
||||
expect(AppController.IS_NOTIFIED).to.be.true;
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
1062
integration/microservices/package-lock.json
generated
1062
integration/microservices/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,21 +7,22 @@
|
||||
"start": "ts-node src/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "5.7.4",
|
||||
"@nestjs/core": "5.7.4",
|
||||
"@nestjs/microservices": "5.7.4",
|
||||
"@nestjs/testing": "5.7.4",
|
||||
"@nestjs/websockets": "5.7.4",
|
||||
"amqp-connection-manager": "2.3.0",
|
||||
"class-transformer": "0.2.0",
|
||||
"class-validator": "0.9.1",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/microservices": "6.8.0",
|
||||
"@nestjs/platform-express": "6.8.0",
|
||||
"@nestjs/testing": "6.8.0",
|
||||
"@nestjs/websockets": "6.8.0",
|
||||
"amqp-connection-manager": "3.0.0",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.10.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"typescript": "3.3.3333"
|
||||
"rxjs": "6.5.3",
|
||||
"typescript": "3.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"@types/node": "7.10.7",
|
||||
"supertest": "4.0.2",
|
||||
"ts-node": "8.0.3"
|
||||
"ts-node": "8.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { Controller, Post, Body, Query, HttpCode } from '@nestjs/common';
|
||||
import { Body, Controller, HttpCode, Post, Query } from '@nestjs/common';
|
||||
import {
|
||||
Client,
|
||||
MessagePattern,
|
||||
ClientProxy,
|
||||
EventPattern,
|
||||
MessagePattern,
|
||||
Transport,
|
||||
} from '@nestjs/microservices';
|
||||
import { Observable, of, from } from 'rxjs';
|
||||
import { from, Observable, of } from 'rxjs';
|
||||
import { scan } from 'rxjs/operators';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
static IS_NOTIFIED = false;
|
||||
|
||||
@Client({ transport: Transport.TCP })
|
||||
client: ClientProxy;
|
||||
|
||||
@@ -62,4 +65,14 @@ export class AppController {
|
||||
streaming(data: number[]): Observable<number> {
|
||||
return from(data);
|
||||
}
|
||||
|
||||
@Post('notify')
|
||||
async sendNotification(): Promise<any> {
|
||||
return this.client.emit<number>('notification', true);
|
||||
}
|
||||
|
||||
@EventPattern('notification')
|
||||
eventHandler(data: boolean) {
|
||||
AppController.IS_NOTIFIED = data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +16,17 @@ export class DisconnectedClientController {
|
||||
const client = ClientProxyFactory.create(options);
|
||||
return client.send<number, number[]>({ cmd: 'none' }, [1, 2, 3]).pipe(
|
||||
/*tap(
|
||||
console.log.bind(console, 'data'),
|
||||
console.error.bind(console, 'error'),
|
||||
),*/
|
||||
catchError(({ code }) =>
|
||||
throwError(
|
||||
console.log.bind(console, 'data'),
|
||||
console.error.bind(console, 'error'),
|
||||
),*/
|
||||
catchError(error => {
|
||||
const { code } = error || { code: 'CONN_ERR' };
|
||||
return throwError(
|
||||
code === 'ECONNREFUSED' || code === 'CONN_ERR'
|
||||
? new RequestTimeoutException('ECONNREFUSED')
|
||||
: new InternalServerErrorException(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
|
||||
import {
|
||||
Client,
|
||||
ClientGrpc,
|
||||
GrpcMethod,
|
||||
GrpcStreamCall,
|
||||
GrpcStreamMethod,
|
||||
Transport,
|
||||
} from '@nestjs/microservices';
|
||||
import { join } from 'path';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
|
||||
@Controller()
|
||||
export class AdvancedGrpcController {
|
||||
/*
|
||||
* HTTP Proxy Client defines loading pattern
|
||||
*/
|
||||
@Client({
|
||||
transport: Transport.GRPC,
|
||||
options: {
|
||||
url: 'localhost:5001',
|
||||
package: 'proto_example.orders',
|
||||
protoPath: 'root.proto',
|
||||
loader: {
|
||||
includeDirs: [join(__dirname, './proto')],
|
||||
keepCase: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
client: ClientGrpc;
|
||||
|
||||
/**
|
||||
* HTTP Proxy entry for support non-stream find method
|
||||
* @param id
|
||||
*/
|
||||
@Post()
|
||||
@HttpCode(200)
|
||||
call(@Body() id: number): Observable<number> {
|
||||
const svc = this.client.getService<any>('OrderService');
|
||||
return svc.find({ id });
|
||||
}
|
||||
|
||||
/**
|
||||
* GRPC stub for Find method
|
||||
* @param id
|
||||
*/
|
||||
@GrpcMethod('orders.OrderService')
|
||||
async find({ id }: { id: number }): Promise<any> {
|
||||
return of({
|
||||
id: 1,
|
||||
itemTypes: [1],
|
||||
shipmentType: {
|
||||
from: 'test',
|
||||
to: 'test1',
|
||||
carrier: 'test-carrier',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* GRPC stub implementation for sync stream method
|
||||
* @param messages
|
||||
*/
|
||||
@GrpcStreamMethod('orders.OrderService')
|
||||
async sync(messages: Observable<any>): Promise<any> {
|
||||
const s = new Subject();
|
||||
const o = s.asObservable();
|
||||
messages.subscribe(msg => {
|
||||
s.next({
|
||||
id: 1,
|
||||
itemTypes: [1],
|
||||
shipmentType: {
|
||||
from: 'test',
|
||||
to: 'test1',
|
||||
carrier: 'test-carrier',
|
||||
},
|
||||
});
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* GRPC stub implementation for syncCall stream method (implemented through call)
|
||||
* @param stream
|
||||
*/
|
||||
@GrpcStreamCall('orders.OrderService')
|
||||
async syncCall(stream: any) {
|
||||
stream.on('data', (msg: any) => {
|
||||
stream.write({
|
||||
id: 1,
|
||||
itemTypes: [1],
|
||||
shipmentType: {
|
||||
from: 'test',
|
||||
to: 'test1',
|
||||
carrier: 'test-carrier',
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
package proto_example.common.items;
|
||||
|
||||
enum ItemType {
|
||||
DEFAULT = 0;
|
||||
SUPERIOR = 1;
|
||||
FLAWLESS = 2;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
syntax = "proto3";
|
||||
package proto_example.common.shipments;
|
||||
|
||||
message ShipmentType {
|
||||
string from = 1;
|
||||
string to = 2;
|
||||
string carrier = 3;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
syntax = "proto3";
|
||||
package proto_example.orders;
|
||||
|
||||
import public "common/item_types.proto";
|
||||
import public "common/shipment_types.proto";
|
||||
|
||||
message Order {
|
||||
int32 id = 1;
|
||||
repeated common.items.ItemType itemTypes = 2;
|
||||
common.shipments.ShipmentType shipmentType = 3;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
syntax = "proto3";
|
||||
import "orders/message.proto";
|
||||
package proto_example.orders;
|
||||
|
||||
service OrderService {
|
||||
rpc Find(Order) returns (Order);
|
||||
rpc Sync(stream Order) returns (stream Order);
|
||||
rpc SyncCall(stream Order) returns (stream Order);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
syntax = "proto3";
|
||||
package proto_example;
|
||||
import public "orders/service.proto";
|
||||
@@ -1,5 +1,12 @@
|
||||
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
|
||||
import { Client, ClientGrpc, GrpcMethod, Transport } from '@nestjs/microservices';
|
||||
import {
|
||||
Client,
|
||||
ClientGrpc,
|
||||
GrpcMethod,
|
||||
GrpcStreamCall,
|
||||
GrpcStreamMethod,
|
||||
Transport,
|
||||
} from '@nestjs/microservices';
|
||||
import { join } from 'path';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
@@ -30,13 +37,6 @@ export class GrpcController {
|
||||
return svc.sum({ data });
|
||||
}
|
||||
|
||||
@Post()
|
||||
@HttpCode(200)
|
||||
call2(@Body() data: number[]): Observable<number> {
|
||||
const svc = this.client2.getService<any>('Math2');
|
||||
return svc.sum({ data });
|
||||
}
|
||||
|
||||
@GrpcMethod('Math')
|
||||
async sum({ data }: { data: number[] }): Promise<any> {
|
||||
return of({
|
||||
@@ -44,10 +44,40 @@ export class GrpcController {
|
||||
});
|
||||
}
|
||||
|
||||
@GrpcStreamMethod('Math')
|
||||
async sumStream(messages: Observable<any>): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
messages.subscribe(
|
||||
msg => {
|
||||
resolve({
|
||||
result: msg.data.reduce((a, b) => a + b),
|
||||
});
|
||||
},
|
||||
err => {
|
||||
reject(err);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@GrpcStreamCall('Math')
|
||||
async sumStreamPass(stream: any) {
|
||||
stream.on('data', (msg: any) => {
|
||||
stream.write({ result: msg.data.reduce((a, b) => a + b) });
|
||||
});
|
||||
}
|
||||
|
||||
@GrpcMethod('Math2')
|
||||
async sum2({ data }: { data: number[] }): Promise<any> {
|
||||
return of({
|
||||
result: data.reduce((a, b) => a + b),
|
||||
});
|
||||
}
|
||||
|
||||
@Post()
|
||||
@HttpCode(200)
|
||||
call2(@Body() data: number[]): Observable<number> {
|
||||
const svc = this.client2.getService<any>('Math2');
|
||||
return svc.sum({ data });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ syntax = "proto3";
|
||||
package math;
|
||||
|
||||
service Math {
|
||||
rpc Sum (RequestSum) returns (SumResult) {}
|
||||
rpc Sum (RequestSum) returns (SumResult);
|
||||
rpc SumStream(stream RequestSum) returns(stream SumResult);
|
||||
rpc SumStreamPass(stream RequestSum) returns(stream SumResult);
|
||||
}
|
||||
|
||||
message SumResult {
|
||||
|
||||
7
integration/microservices/src/kafka/dtos/business.dto.ts
Normal file
7
integration/microservices/src/kafka/dtos/business.dto.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { UserEntity } from '../entities/user.entity';
|
||||
|
||||
export class BusinessDto {
|
||||
name: string;
|
||||
phone: string;
|
||||
user: UserEntity;
|
||||
}
|
||||
6
integration/microservices/src/kafka/dtos/user.dto.ts
Normal file
6
integration/microservices/src/kafka/dtos/user.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export class UserDto {
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
years: number;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { UserEntity } from './user.entity';
|
||||
import { BusinessDto } from '../dtos/business.dto';
|
||||
|
||||
export class BusinessEntity {
|
||||
constructor(business: BusinessDto) {
|
||||
this.id = Math.random() * 99999999;
|
||||
this.name = business.name;
|
||||
this.phone = business.phone;
|
||||
this.createdBy = {
|
||||
id: business.user.id,
|
||||
};
|
||||
this.created = new Date();
|
||||
}
|
||||
id: number;
|
||||
name: string;
|
||||
phone: string;
|
||||
createdBy: Partial<UserEntity>;
|
||||
created: Date;
|
||||
}
|
||||
18
integration/microservices/src/kafka/entities/user.entity.ts
Normal file
18
integration/microservices/src/kafka/entities/user.entity.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { UserDto } from '../dtos/user.dto';
|
||||
|
||||
export class UserEntity {
|
||||
constructor(user: UserDto) {
|
||||
this.id = Math.random() * 99999999;
|
||||
this.name = user.name;
|
||||
this.email = user.email;
|
||||
this.phone = user.phone;
|
||||
this.years = user.years;
|
||||
this.created = new Date();
|
||||
}
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
years: number;
|
||||
created: Date;
|
||||
}
|
||||
152
integration/microservices/src/kafka/kafka.controller.ts
Normal file
152
integration/microservices/src/kafka/kafka.controller.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { Body, Controller, HttpCode, OnModuleInit, Post } from '@nestjs/common';
|
||||
import { Logger } from '@nestjs/common/services/logger.service';
|
||||
import { Client, ClientKafka, Transport } from '@nestjs/microservices';
|
||||
import { Observable } from 'rxjs';
|
||||
import { BusinessDto } from './dtos/business.dto';
|
||||
import { UserDto } from './dtos/user.dto';
|
||||
|
||||
@Controller()
|
||||
export class KafkaController implements OnModuleInit {
|
||||
protected readonly logger = new Logger(KafkaController.name);
|
||||
static IS_NOTIFIED = false;
|
||||
static MATH_SUM = 0;
|
||||
|
||||
@Client({
|
||||
transport: Transport.KAFKA,
|
||||
options: {
|
||||
client: {
|
||||
brokers: ['localhost:9092'],
|
||||
},
|
||||
},
|
||||
})
|
||||
private readonly client: ClientKafka;
|
||||
|
||||
onModuleInit() {
|
||||
const requestPatterns = [
|
||||
'math.sum.sync.kafka.message',
|
||||
'math.sum.sync.without.key',
|
||||
'math.sum.sync.plain.object',
|
||||
'math.sum.sync.array',
|
||||
'math.sum.sync.string',
|
||||
'math.sum.sync.number',
|
||||
'user.create',
|
||||
'business.create',
|
||||
];
|
||||
|
||||
requestPatterns.forEach(pattern => {
|
||||
this.client.subscribeToResponseOf(pattern);
|
||||
});
|
||||
}
|
||||
|
||||
// sync send kafka message
|
||||
@Post('mathSumSyncKafkaMessage')
|
||||
@HttpCode(200)
|
||||
async mathSumSyncKafkaMessage(
|
||||
@Body() data: number[],
|
||||
): Promise<Observable<any>> {
|
||||
const result = await this.client
|
||||
.send('math.sum.sync.kafka.message', {
|
||||
key: '1',
|
||||
value: {
|
||||
numbers: data,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
|
||||
// sync send kafka(ish) message without key and only the value
|
||||
@Post('mathSumSyncWithoutKey')
|
||||
@HttpCode(200)
|
||||
async mathSumSyncWithoutKey(
|
||||
@Body() data: number[],
|
||||
): Promise<Observable<any>> {
|
||||
const result = await this.client
|
||||
.send('math.sum.sync.without.key', {
|
||||
value: {
|
||||
numbers: data,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
|
||||
// sync send message without key or value
|
||||
@Post('mathSumSyncPlainObject')
|
||||
@HttpCode(200)
|
||||
async mathSumSyncPlainObject(
|
||||
@Body() data: number[],
|
||||
): Promise<Observable<any>> {
|
||||
const result = await this.client
|
||||
.send('math.sum.sync.plain.object', {
|
||||
numbers: data,
|
||||
})
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
|
||||
// sync send message without key or value
|
||||
@Post('mathSumSyncArray')
|
||||
@HttpCode(200)
|
||||
async mathSumSyncArray(@Body() data: number[]): Promise<Observable<any>> {
|
||||
const result = await this.client
|
||||
.send('math.sum.sync.array', data)
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Post('mathSumSyncString')
|
||||
@HttpCode(200)
|
||||
async mathSumSyncString(@Body() data: number[]): Promise<Observable<any>> {
|
||||
// this.logger.error(util.format('mathSumSyncString() data: %o', data));
|
||||
const result = await this.client
|
||||
.send('math.sum.sync.string', data.toString())
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Post('mathSumSyncNumber')
|
||||
@HttpCode(200)
|
||||
async mathSumSyncNumber(@Body() data: number[]): Promise<Observable<any>> {
|
||||
const result = await this.client
|
||||
.send('math.sum.sync.number', data[0])
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
|
||||
// async notify
|
||||
@Post('notify')
|
||||
async sendNotification(): Promise<any> {
|
||||
return this.client.emit('notify', { notify: true });
|
||||
}
|
||||
|
||||
// Complex data to send.
|
||||
@Post('/user')
|
||||
@HttpCode(200)
|
||||
async createUser(@Body() user: UserDto): Promise<Observable<any>> {
|
||||
const result = await this.client
|
||||
.send('user.create', {
|
||||
key: '1',
|
||||
value: {
|
||||
user,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Complex data to send.
|
||||
@Post('/business')
|
||||
@HttpCode(200)
|
||||
async createBusiness(@Body() business: BusinessDto) {
|
||||
const result = await this.client
|
||||
.send('business.create', {
|
||||
key: '1',
|
||||
value: {
|
||||
business,
|
||||
},
|
||||
})
|
||||
.toPromise();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { Logger } from '@nestjs/common/services/logger.service';
|
||||
import { EventPattern, MessagePattern } from '@nestjs/microservices';
|
||||
import { BusinessDto } from './dtos/business.dto';
|
||||
import { UserDto } from './dtos/user.dto';
|
||||
import { BusinessEntity } from './entities/business.entity';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import { KafkaController } from './kafka.controller';
|
||||
|
||||
@Controller()
|
||||
export class KafkaMessagesController {
|
||||
protected readonly logger = new Logger(KafkaMessagesController.name);
|
||||
static IS_NOTIFIED = false;
|
||||
|
||||
@MessagePattern('math.sum.sync.kafka.message')
|
||||
mathSumSyncKafkaMessage(data: any) {
|
||||
return (data.value.numbers || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@MessagePattern('math.sum.sync.without.key')
|
||||
mathSumSyncWithoutKey(data: any) {
|
||||
return (data.value.numbers || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@MessagePattern('math.sum.sync.plain.object')
|
||||
mathSumSyncPlainObject(data: any) {
|
||||
return (data.value.numbers || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@MessagePattern('math.sum.sync.array')
|
||||
mathSumSyncArray(data: any) {
|
||||
return (data.value || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@MessagePattern('math.sum.sync.string')
|
||||
mathSumSyncString(data: any) {
|
||||
// this.logger.error(util.format('mathSumSyncString() data: %o', data));
|
||||
return (data.value.split(',') || [])
|
||||
.map(i => {
|
||||
return parseFloat(i);
|
||||
})
|
||||
.reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@MessagePattern('math.sum.sync.number')
|
||||
mathSumSyncNumber(data: any) {
|
||||
// this.logger.error(util.format('mathSumSyncNumber() data: %o', data));
|
||||
return (data.value.toString().split('') || [])
|
||||
.map(i => {
|
||||
return parseFloat(i);
|
||||
})
|
||||
.reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@EventPattern('notify')
|
||||
eventHandler(data: any) {
|
||||
KafkaController.IS_NOTIFIED = data.value.notify;
|
||||
}
|
||||
|
||||
// Complex data to send.
|
||||
@MessagePattern('user.create')
|
||||
async createUser(params: { value: { user: UserDto } }) {
|
||||
return new UserEntity(params.value.user);
|
||||
}
|
||||
|
||||
@MessagePattern('business.create')
|
||||
async createBusiness(params: { value: { business: BusinessDto } }) {
|
||||
return new BusinessEntity(params.value.business);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@ import { Body, Controller, Get, HttpCode, Post, Query } from '@nestjs/common';
|
||||
import {
|
||||
Client,
|
||||
ClientProxy,
|
||||
Ctx,
|
||||
EventPattern,
|
||||
MessagePattern,
|
||||
NatsContext,
|
||||
Payload,
|
||||
RpcException,
|
||||
Transport,
|
||||
} from '@nestjs/microservices';
|
||||
@@ -57,7 +60,7 @@ export class NatsController {
|
||||
}
|
||||
|
||||
@MessagePattern('math.*')
|
||||
sum(data: number[]): number {
|
||||
sum(@Payload() data: number[], @Ctx() context: NatsContext): number {
|
||||
return (data || []).reduce((a, b) => a + b);
|
||||
}
|
||||
|
||||
@@ -94,7 +97,7 @@ export class NatsController {
|
||||
}
|
||||
|
||||
@EventPattern('notification')
|
||||
eventHandler(data: boolean) {
|
||||
eventHandler(@Payload() data: boolean) {
|
||||
NatsController.IS_NOTIFIED = data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export class RMQBroadcastController {
|
||||
urls: [`amqp://localhost:5672`],
|
||||
queue: 'test_broadcast',
|
||||
queueOptions: { durable: false },
|
||||
socketOptions: { noDelay: true },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export class RMQController {
|
||||
urls: [`amqp://localhost:5672`],
|
||||
queue: 'test',
|
||||
queueOptions: { durable: false },
|
||||
socketOptions: { noDelay: true },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
1176
integration/mongoose/package-lock.json
generated
1176
integration/mongoose/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,18 +9,18 @@
|
||||
"start:prod": "node dist/main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "5.7.4",
|
||||
"@nestjs/core": "5.7.4",
|
||||
"@nestjs/mongoose": "6.0.0",
|
||||
"mongoose": "5.4.19",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/mongoose": "6.1.2",
|
||||
"mongoose": "5.7.3",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"typescript": "2.9.2"
|
||||
"rxjs": "6.5.3",
|
||||
"typescript": "3.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mongoose": "5.3.23",
|
||||
"@types/node": "7.10.5",
|
||||
"ts-node": "8.0.3",
|
||||
"tslint": "5.14.0"
|
||||
"@types/mongoose": "5.5.18",
|
||||
"@types/node": "7.10.7",
|
||||
"ts-node": "8.4.1",
|
||||
"tslint": "5.20.0"
|
||||
}
|
||||
}
|
||||
|
||||
81
integration/scopes/e2e/msvc-request-scope.spec.ts
Normal file
81
integration/scopes/e2e/msvc-request-scope.spec.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Transport } from '@nestjs/microservices';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { Guard } from '../src/msvc/guards/request-scoped.guard';
|
||||
import { HelloController } from '../src/msvc/hello.controller';
|
||||
import { HelloModule } from '../src/msvc/hello.module';
|
||||
import { Interceptor } from '../src/msvc/interceptors/logging.interceptor';
|
||||
import { UsersService } from '../src/msvc/users/users.service';
|
||||
|
||||
class Meta {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
Meta.COUNTER++;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Request scope (microservices)', () => {
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
before(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
HelloModule.forRoot({
|
||||
provide: 'META',
|
||||
useClass: Meta,
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
app.connectMicroservice({ transport: Transport.TCP });
|
||||
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
await app.startAllMicroservicesAsync();
|
||||
});
|
||||
|
||||
describe('when one service is request scoped', () => {
|
||||
before(async () => {
|
||||
const performHttpCall = end =>
|
||||
request(server)
|
||||
.get('/hello')
|
||||
.end((err, res) => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
await new Promise(resolve => performHttpCall(resolve));
|
||||
});
|
||||
|
||||
it(`should create controller for each request`, async () => {
|
||||
expect(HelloController.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should create service for each request`, async () => {
|
||||
expect(UsersService.COUNTER).to.be.eql(3);
|
||||
});
|
||||
|
||||
it(`should share static provider across requests`, async () => {
|
||||
expect(Meta.COUNTER).to.be.eql(1);
|
||||
});
|
||||
|
||||
it(`should create request scoped interceptor for each request`, async () => {
|
||||
expect(Interceptor.COUNTER).to.be.eql(3);
|
||||
expect(Interceptor.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]);
|
||||
});
|
||||
|
||||
it(`should create request scoped guard for each request`, async () => {
|
||||
expect(Guard.COUNTER).to.be.eql(3);
|
||||
expect(Guard.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
@@ -63,14 +63,17 @@ describe('Request scope', () => {
|
||||
|
||||
it(`should create request scoped pipe for each request`, async () => {
|
||||
expect(UserByIdPipe.COUNTER).to.be.eql(3);
|
||||
expect(UserByIdPipe.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]);
|
||||
});
|
||||
|
||||
it(`should create request scoped interceptor for each request`, async () => {
|
||||
expect(Interceptor.COUNTER).to.be.eql(3);
|
||||
expect(Interceptor.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]);
|
||||
});
|
||||
|
||||
it(`should create request scoped guard for each request`, async () => {
|
||||
expect(Guard.COUNTER).to.be.eql(3);
|
||||
expect(Guard.REQUEST_SCOPED_DATA).to.deep.equal([1, 1, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
1388
integration/scopes/package-lock.json
generated
1388
integration/scopes/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -7,21 +7,21 @@
|
||||
"start": "ts-node src/main"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "5.7.4",
|
||||
"@nestjs/core": "5.7.4",
|
||||
"@nestjs/microservices": "5.7.4",
|
||||
"@nestjs/testing": "5.7.4",
|
||||
"@nestjs/websockets": "5.7.4",
|
||||
"class-transformer": "0.2.0",
|
||||
"class-validator": "0.9.1",
|
||||
"fastify": "2.1.0",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/microservices": "6.8.0",
|
||||
"@nestjs/testing": "6.8.0",
|
||||
"@nestjs/websockets": "6.8.0",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.10.1",
|
||||
"fastify": "2.9.0",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"typescript": "2.9.2"
|
||||
"rxjs": "6.5.3",
|
||||
"typescript": "3.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"@types/node": "7.10.7",
|
||||
"supertest": "4.0.2",
|
||||
"ts-node": "8.0.3"
|
||||
"ts-node": "8.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
Injectable,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
@@ -9,13 +10,16 @@ import { Observable } from 'rxjs';
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Guard implements CanActivate {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
static REQUEST_SCOPED_DATA = [];
|
||||
|
||||
constructor(@Inject('REQUEST_ID') private requestId: number) {
|
||||
Guard.COUNTER++;
|
||||
}
|
||||
|
||||
canActivate(
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
Guard.REQUEST_SCOPED_DATA.push(this.requestId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import { DynamicModule, Inject, Module, Provider } from '@nestjs/common';
|
||||
import { DynamicModule, Inject, Module, Provider, Scope } from '@nestjs/common';
|
||||
import { HelloController } from './hello.controller';
|
||||
import { HelloService } from './hello.service';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HelloController],
|
||||
providers: [HelloService, UsersService],
|
||||
providers: [
|
||||
HelloService,
|
||||
UsersService,
|
||||
{
|
||||
provide: 'REQUEST_ID',
|
||||
useFactory: () => 1,
|
||||
scope: Scope.REQUEST,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class HelloModule {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
Scope,
|
||||
@@ -10,10 +11,14 @@ import { Observable } from 'rxjs';
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Interceptor implements NestInterceptor {
|
||||
static COUNTER = 0;
|
||||
constructor() {
|
||||
static REQUEST_SCOPED_DATA = [];
|
||||
|
||||
constructor(@Inject('REQUEST_ID') private requestId: number) {
|
||||
Interceptor.COUNTER++;
|
||||
}
|
||||
|
||||
intercept(context: ExecutionContext, call: CallHandler): Observable<any> {
|
||||
Interceptor.REQUEST_SCOPED_DATA.push(this.requestId);
|
||||
return call.handle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
|
||||
import {
|
||||
ArgumentMetadata,
|
||||
Inject,
|
||||
Injectable,
|
||||
PipeTransform,
|
||||
} from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Injectable()
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
static COUNTER = 0;
|
||||
constructor(private readonly usersService: UsersService) {
|
||||
static REQUEST_SCOPED_DATA = [];
|
||||
|
||||
constructor(
|
||||
@Inject('REQUEST_ID') private requestId: number,
|
||||
private readonly usersService: UsersService,
|
||||
) {
|
||||
UserByIdPipe.COUNTER++;
|
||||
}
|
||||
|
||||
transform(value: string, metadata: ArgumentMetadata) {
|
||||
UserByIdPipe.REQUEST_SCOPED_DATA.push(this.requestId);
|
||||
return this.usersService.findById(value);
|
||||
}
|
||||
}
|
||||
|
||||
10
integration/scopes/src/msvc/dto/test.dto.ts
Normal file
10
integration/scopes/src/msvc/dto/test.dto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { IsString, IsNotEmpty, IsNumber } from 'class-validator';
|
||||
|
||||
export class TestDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
string: string;
|
||||
|
||||
@IsNumber()
|
||||
number: number;
|
||||
}
|
||||
25
integration/scopes/src/msvc/guards/request-scoped.guard.ts
Normal file
25
integration/scopes/src/msvc/guards/request-scoped.guard.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
Injectable,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Guard implements CanActivate {
|
||||
static COUNTER = 0;
|
||||
static REQUEST_SCOPED_DATA = [];
|
||||
|
||||
constructor(@Inject('REQUEST_ID') private requestId: number) {
|
||||
Guard.COUNTER++;
|
||||
}
|
||||
|
||||
canActivate(
|
||||
context: ExecutionContext,
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
Guard.REQUEST_SCOPED_DATA.push(this.requestId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
24
integration/scopes/src/msvc/hello.controller.ts
Normal file
24
integration/scopes/src/msvc/hello.controller.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Controller, UseGuards, UseInterceptors } from '@nestjs/common';
|
||||
import { MessagePattern } from '@nestjs/microservices';
|
||||
import { Guard } from './guards/request-scoped.guard';
|
||||
import { HelloService } from './hello.service';
|
||||
import { Interceptor } from './interceptors/logging.interceptor';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Controller()
|
||||
export class HelloController {
|
||||
static COUNTER = 0;
|
||||
constructor(
|
||||
private readonly helloService: HelloService,
|
||||
private readonly usersService: UsersService,
|
||||
) {
|
||||
HelloController.COUNTER++;
|
||||
}
|
||||
|
||||
@UseGuards(Guard)
|
||||
@UseInterceptors(Interceptor)
|
||||
@MessagePattern('test')
|
||||
greeting(): string {
|
||||
return this.helloService.greeting();
|
||||
}
|
||||
}
|
||||
28
integration/scopes/src/msvc/hello.module.ts
Normal file
28
integration/scopes/src/msvc/hello.module.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { DynamicModule, Inject, Module, Provider, Scope } from '@nestjs/common';
|
||||
import { HelloController } from './hello.controller';
|
||||
import { HelloService } from './hello.service';
|
||||
import { HttpController } from './http.controller';
|
||||
import { UsersService } from './users/users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [HelloController, HttpController],
|
||||
providers: [
|
||||
HelloService,
|
||||
UsersService,
|
||||
{
|
||||
provide: 'REQUEST_ID',
|
||||
useFactory: () => 1,
|
||||
scope: Scope.REQUEST,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class HelloModule {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
static forRoot(meta: Provider): DynamicModule {
|
||||
return {
|
||||
module: HelloModule,
|
||||
providers: [meta],
|
||||
};
|
||||
}
|
||||
}
|
||||
10
integration/scopes/src/msvc/hello.service.ts
Normal file
10
integration/scopes/src/msvc/hello.service.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class HelloService {
|
||||
constructor(@Inject('META') private readonly meta) {}
|
||||
|
||||
greeting(): string {
|
||||
return 'Hello world!';
|
||||
}
|
||||
}
|
||||
13
integration/scopes/src/msvc/http.controller.ts
Normal file
13
integration/scopes/src/msvc/http.controller.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ClientProxyFactory, Transport } from '@nestjs/microservices';
|
||||
|
||||
@Controller()
|
||||
export class HttpController {
|
||||
@Get('hello')
|
||||
testMsvc() {
|
||||
const client = ClientProxyFactory.create({
|
||||
transport: Transport.TCP,
|
||||
});
|
||||
return client.send('test', { test: true });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
Scope,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class Interceptor implements NestInterceptor {
|
||||
static COUNTER = 0;
|
||||
static REQUEST_SCOPED_DATA = [];
|
||||
|
||||
constructor(@Inject('REQUEST_ID') private requestId: number) {
|
||||
Interceptor.COUNTER++;
|
||||
}
|
||||
|
||||
intercept(context: ExecutionContext, call: CallHandler): Observable<any> {
|
||||
Interceptor.REQUEST_SCOPED_DATA.push(this.requestId);
|
||||
return call.handle();
|
||||
}
|
||||
}
|
||||
13
integration/scopes/src/msvc/users/users.service.ts
Normal file
13
integration/scopes/src/msvc/users/users.service.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Inject, Injectable, Scope } from '@nestjs/common';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class UsersService {
|
||||
static COUNTER = 0;
|
||||
constructor(@Inject('META') private readonly meta) {
|
||||
UsersService.COUNTER++;
|
||||
}
|
||||
|
||||
findById(id: string) {
|
||||
return { id };
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
|
||||
describe('GraphQL - Pipes', () => {
|
||||
describe('GraphQL Pipes', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
3220
integration/typegraphql/package-lock.json
generated
3220
integration/typegraphql/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,24 +9,24 @@
|
||||
"start:prod": "node dist/main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "6.0.1",
|
||||
"@nestjs/core": "6.0.1",
|
||||
"@nestjs/graphql": "6.0.1",
|
||||
"apollo-server-express": "2.4.8",
|
||||
"class-transformer": "0.2.0",
|
||||
"class-validator": "0.9.1",
|
||||
"graphql": "14.1.1",
|
||||
"graphql-tools": "4.0.4",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/graphql": "6.5.3",
|
||||
"apollo-server-express": "2.9.4",
|
||||
"class-transformer": "0.2.3",
|
||||
"class-validator": "0.10.1",
|
||||
"graphql": "14.5.8",
|
||||
"graphql-tools": "4.0.5",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"rxjs": "6.5.3",
|
||||
"subscriptions-transport-ws": "0.9.16",
|
||||
"type-graphql": "0.17.0",
|
||||
"typescript": "3.3.3",
|
||||
"ws": "4.1.0"
|
||||
"type-graphql": "0.17.5",
|
||||
"typescript": "3.6.3",
|
||||
"ws": "7.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"ts-node": "8.0.3",
|
||||
"tslint": "5.14.0"
|
||||
"@types/node": "7.10.7",
|
||||
"ts-node": "8.4.1",
|
||||
"tslint": "5.20.0"
|
||||
}
|
||||
}
|
||||
|
||||
1790
integration/typeorm/package-lock.json
generated
1790
integration/typeorm/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -9,18 +9,18 @@
|
||||
"start:prod": "node dist/main.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "5.7.4",
|
||||
"@nestjs/core": "5.7.4",
|
||||
"@nestjs/typeorm": "6.0.0",
|
||||
"mysql": "2.16.0",
|
||||
"@nestjs/common": "6.8.0",
|
||||
"@nestjs/core": "6.8.0",
|
||||
"@nestjs/typeorm": "6.2.0",
|
||||
"mysql": "2.17.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"rxjs": "6.4.0",
|
||||
"typeorm": "0.2.15",
|
||||
"typescript": "2.9.2"
|
||||
"rxjs": "6.5.3",
|
||||
"typeorm": "0.2.19",
|
||||
"typescript": "3.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "7.10.5",
|
||||
"ts-node": "8.0.3",
|
||||
"tslint": "5.14.0"
|
||||
"@types/node": "7.10.7",
|
||||
"ts-node": "8.4.1",
|
||||
"tslint": "5.20.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,11 @@ async function createNestApp(...gateways): Promise<INestApplication> {
|
||||
providers: gateways,
|
||||
}).compile();
|
||||
const app = await testingModule.createNestApplication();
|
||||
app.useWebSocketAdapter(new WsAdapter(app));
|
||||
app.useWebSocketAdapter(new WsAdapter(app) as any);
|
||||
return app;
|
||||
}
|
||||
|
||||
describe('WebSocketGateway (WsAdapter)', () => {
|
||||
const event = 'push';
|
||||
let ws, ws2, app;
|
||||
|
||||
it(`should handle message (2nd port)`, async () => {
|
||||
@@ -66,10 +65,15 @@ describe('WebSocketGateway (WsAdapter)', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it(`should support 2 different gateways`, async () => {
|
||||
it(`should support 2 different gateways`, async function() {
|
||||
this.retries(10);
|
||||
|
||||
app = await createNestApp(ApplicationGateway, CoreGateway);
|
||||
await app.listenAsync(3000);
|
||||
|
||||
// open websockets delay
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
ws = new WebSocket('ws://localhost:8080');
|
||||
ws2 = new WebSocket('ws://localhost:8090');
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user