├── .github └── workflows │ ├── CD.yml │ └── CI.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── .prettierrc ├── LICENCE.md ├── README.md ├── codecov.yml ├── docker-compose.yml ├── docker ├── Dockerfile └── rabbitmq │ └── enabled_plugins ├── examples ├── server1 │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── nest-cli.json │ ├── package.json │ ├── src │ │ ├── app.controller.spec.ts │ │ ├── app.controller.ts │ │ ├── app.module.ts │ │ ├── app.service.ts │ │ ├── events │ │ │ ├── default.event.ts │ │ │ └── rabbit.event.ts │ │ ├── handlers │ │ │ ├── default.event.handler.ts │ │ │ └── rabbit.event.handler.ts │ │ ├── main.ts │ │ └── publishers │ │ │ └── rabbit.publisher.ts │ ├── test │ │ ├── app.e2e-spec.ts │ │ └── jest-e2e.json │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tslint.json └── server2 │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── nest-cli.json │ ├── package.json │ ├── src │ ├── app.controller.spec.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── events │ │ └── rabbit.event.ts │ ├── handlers │ │ └── rabbit.event.handler.ts │ └── main.ts │ ├── test │ ├── app.e2e-spec.ts │ └── jest-e2e.json │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tslint.json ├── index.ts ├── nest-cli.json ├── package.json ├── src ├── constants │ └── transport.event-bus.constants.ts ├── decorators │ ├── transport.event.event-bus.decarator.ts │ ├── transport.exclude-def.decorator.ts │ ├── transport.publisher.event-bus.decorator.ts │ └── transport.type.event-bus.decorator.ts ├── index.ts ├── interfaces │ ├── transport.data.event-bus.interface.ts │ └── transport.publisher.event-bus.interface.ts ├── transport.event-bus.module.ts ├── transport.event-bus.publisher.ts └── transport.event-bus.service.ts ├── test ├── int │ ├── jest-int.json │ └── transport-eventbus │ │ ├── commands │ │ ├── try.aggregate-root.command.ts │ │ └── try.saga.command.ts │ │ ├── eventbus.int.test.ts │ │ ├── events │ │ └── test.events.ts │ │ ├── handlers │ │ ├── default.event.handler.ts │ │ ├── try.aggregate-root.command.handler.ts │ │ ├── try.aggregate-root.event.handler.ts │ │ ├── try.saga.command.handler.ts │ │ └── try.saga.handler.ts │ │ ├── model │ │ └── test.model.ts │ │ ├── publishers │ │ └── rabbit.publisher.ts │ │ ├── service │ │ └── test.service.ts │ │ └── storage │ │ └── storage.ts └── jest.json ├── tsconfig.build.json ├── tsconfig.json └── tslint.json /.github/workflows/CD.yml: -------------------------------------------------------------------------------- 1 | name: Nest Transport CD 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | release: 8 | name: Publish Package 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | 14 | # Use this GitHub Action 15 | - name: Check package version 16 | uses: technote-space/package-version-check-action@v1 17 | with: 18 | COMMIT_DISABLED: 1 19 | 20 | - name: Install Package dependencies 21 | run: npm install 22 | - name: Build 23 | run: npm run build 24 | - name: Publish 25 | run: | 26 | npm config set //registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN 27 | npm publish 28 | env: 29 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: Nest Transport CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [10.x, 12.x, 13.x, 14.x, 15.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | - run: npm run build --if-present 22 | - run: npm test 23 | env: 24 | CI: true 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | index.ts 3 | package-lock.json 4 | tslint.json 5 | tsconfig.json 6 | .prettierrc 7 | .idea 8 | tests 9 | test 10 | examples 11 | node_modules 12 | docker 13 | docker-compose.yml 14 | .travis.yml 15 | codecov.yml 16 | nest-cli.json 17 | tsconfig.build.json -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /LICENCE.md: -------------------------------------------------------------------------------- 1 | Copyright 2 | 3 | (c) 2019, Sergey Telpuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI](https://github.com/sergey-telpuk/nestjs-transport-eventbus/actions/workflows/CI.yml/badge.svg)](https://github.com/sergey-telpuk/nestjs-transport-eventbus/actions/workflows/CI.yml) 2 | [![CD](https://github.com/sergey-telpuk/nestjs-transport-eventbus/actions/workflows/CD.yml/badge.svg)](https://github.com/sergey-telpuk/nestjs-transport-eventbus/actions/workflows/CD.yml) 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/sergey-telpuk/nestjs-transport-eventbus.svg)](https://greenkeeper.io/) 4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/sergey-telpuk/nestjs-transport-eventbus/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/sergey-telpuk/nestjs-transport-eventbus/) 5 | [![codecov](https://codecov.io/gh/sergey-telpuk/nestjs-transport-eventbus/branch/master/graph/badge.svg)](https://codecov.io/gh/sergey-telpuk/nestjs-transport-eventbus) 6 | [![npm](https://img.shields.io/npm/dw/nestjs-transport-eventbus)](https://www.npmjs.com/package/nestjs-transport-eventbus) 7 | 8 | ## Description 9 | The **nestjs-transport-eventbus** module for [Nest](https://github.com/nestjs/nest).\ 10 | **nestjs-transport-eventbus** allows broadcasting events via variety of [nestjs trasports](https://docs.nestjs.com/microservices/basics) in easy way 11 | ## Installation 12 | npm i nestjs-transport-eventbus 13 | 14 | ## Quick Start 15 | Import `TransportEventBusModule` into a willing module, example below: 16 | ```typescript 17 | import { TransportEventBusModule } from 'nestjs-transport-eventbus'; 18 | 19 | @Module({ 20 | imports: [ 21 | TransportEventBusModule 22 | ], 23 | controllers: [AppController], 24 | providers: [ 25 | AppService 26 | ], 27 | }) 28 | export class AppModule { 29 | } 30 | ``` 31 | `TransportEventBusModule` applies two arguments:\ 32 | `publishers` - array of transport publishers which are based on `ClientProxy`\ 33 | `providers` - the additional providers for module 34 | 35 | ### Example with RabbitMQ(RabbitPublisher) 36 | For creating a transport publisher there is enough to implement the following steps: 37 | 1. Implement `RabbitPublisher`, example below:\ 38 | ```typescript 39 | import { Injectable } from '@nestjs/common'; 40 | import { ClientProxy, Transport, Client} from '@nestjs/microservices'; 41 | import { Publisher } from 'nestjs-transport-eventbus'; 42 | 43 | @Injectable() 44 | @Publisher(Transport.RMQ)//Choose the appropriate type of transport in this case `RMQ` 45 | export class RabbitPublisher { 46 | @Client({ 47 | transport: Transport.RMQ, 48 | options: { 49 | urls: ['amqp://rabbit:rabbit@rabbitmq:5672'], 50 | queue: 'event_service_queue', 51 | queueOptions: { 52 | durable: true, 53 | }, 54 | }, 55 | }) 56 | client: ClientProxy; 57 | } 58 | ``` 59 | 2. Inject `RabbitPublisher` into `TransportEventBusModule`, example below: 60 | ```typescript 61 | import { Module } from '@nestjs/common'; 62 | import { TransportEventBusModule } from 'nestjs-transport-eventbus'; 63 | import { RabbitPublisher } from '...'; 64 | 65 | @Module({ 66 | imports: [ 67 | TransportEventBusModule.forRoot({ 68 | publishers: [RabbitPublisher] 69 | }) 70 | ], 71 | controllers: [], 72 | providers: [ 73 | 74 | ], 75 | }) 76 | export class AppModule { 77 | } 78 | ``` 79 | 3. Create an event for publisher, example below: 80 | ```typescript 81 | import { TransportType, ExcludeDef } from 'nestjs-transport-eventbus'; 82 | import { Transport } from '@nestjs/microservices'; 83 | 84 | @TransportType(Transport.RMQ) 85 | export class RabbitEvent { 86 | constructor( 87 | readonly message: string 88 | ) { 89 | } 90 | } 91 | ``` 92 | > Notice: By default, events are handling on both sides broadcasting server and receiving server, for changing this situation add `@ExcludeDef()` declaration, 93 | >can look like below: 94 | >\ 95 | >....\ 96 | >@TransportType(Transport.RMQ)\ 97 | >@ExcludeDef()\ 98 | >export class RabbitEvent\ 99 | >... 100 | > 101 | 4.Inject `TRANSPORT_EVENT_BUS_SERVICE`, example below: 102 | ```typescript 103 | import { Inject, Injectable } from '@nestjs/common'; 104 | import { TRANSPORT_EVENT_BUS_SERVICE } from 'nestjs-transport-eventbus'; 105 | import { IEventBus } from '@nestjs/cqrs'; 106 | import { DefaultEvent } from '...'; 107 | import { RabbitEvent } from '...' 108 | @Injectable() 109 | export class AppService { 110 | constructor( 111 | @Inject(TRANSPORT_EVENT_BUS_SERVICE) private readonly eventBus: IEventBus 112 | ){ 113 | 114 | } 115 | rabbitEvent(): void { 116 | this.eventBus.publish(new RabbitEvent('Pass some param')); 117 | } 118 | } 119 | ``` 120 | > Notice: Events via [AggregateRoot](https://docs.nestjs.com/recipes/cqrs#events) 121 | ```typescript 122 | import { CommandHandler, EventPublisher, ICommandHandler } from '@nestjs/cqrs'; 123 | import { TryAggregateRootCommand } from '...'; 124 | import { Inject } from '@nestjs/common'; 125 | import { TRANSPORT_EVENT_BUS_PUBLISHER } from 'nestjs-transport-eventbus'; 126 | import { TestModel } from '...'; 127 | 128 | @CommandHandler(TryAggregateRootCommand) 129 | export class TryAggregateRootCommandHandler implements ICommandHandler { 130 | constructor( 131 | @Inject(TRANSPORT_EVENT_BUS_PUBLISHER) private readonly publisher: EventPublisher 132 | ) { 133 | } 134 | 135 | async execute(command: TryAggregateRootCommand) { 136 | const {message} = command; 137 | const aggregator = this.publisher.mergeObjectContext( 138 | new TestModel() 139 | ); 140 | aggregator.applyEvent(message); 141 | aggregator.commit(); 142 | } 143 | } 144 | ``` 145 | 5. For handling the broadcasted event on receiving side can look like the following: 146 | ```typescript 147 | import { Controller, Inject } from '@nestjs/common'; 148 | import { EventPattern } from '@nestjs/microservices'; 149 | import { TRANSPORT_EVENT_BUS_PATTERN, TRANSPORT_EVENT_BUS_SERVICE, TransportEvent } from 'nestjs-transport-eventbus'; 150 | import { IEvent, IEventBus } from '@nestjs/cqrs'; 151 | 152 | @Controller() 153 | export class AppService { 154 | 155 | constructor( 156 | @Inject(TRANSPORT_EVENT_BUS_SERVICE) private readonly eventBus: IEventBus 157 | ){ 158 | 159 | } 160 | @EventPattern(TRANSPORT_EVENT_BUS_PATTERN) 161 | handle(@TransportEvent() event: IEvent): void { 162 | this.eventBus.publish(event); 163 | } 164 | ``` 165 | > Notice: `TRANSPORT_EVENT_BUS_PATTERN`- can pass via .env 166 | ### Examples 167 | [Example is presented two services which communicate each other via RabbitMQ](https://github.com/sergey-telpuk/nestjs-transport-eventbus/tree/master/examples) 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | token: bd7b63ad-24ff-47c5-811a-f64ed7421ded -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | transport-event-module: 5 | container_name: transport-event-module 6 | # command: npm run test 7 | command: tail -f /dev/null 8 | build: 9 | context: ./docker 10 | dockerfile: Dockerfile 11 | ports: 12 | - 3001:3001 13 | - 3002:3002 14 | - 3003:3003 15 | tty: true 16 | volumes: 17 | - .:/app 18 | 19 | rabbitmq: 20 | image: rabbitmq:3-management 21 | restart: always 22 | logging: 23 | driver: none 24 | environment: 25 | RABBITMQ_ERLANG_COOKIE: SWQOKODSQALRPCLNMEQG 26 | RABBITMQ_DEFAULT_USER: rabbit 27 | RABBITMQ_DEFAULT_PASS: rabbit 28 | volumes: 29 | - ./docker/rabbitmq/enabled_plugins:/etc/rabbitmq/enabled_plugin 30 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | 3 | WORKDIR /app 4 | 5 | RUN npm i -g @nestjs/cli 6 | 7 | EXPOSE 9229 8 | 9 | CMD ["node","--v"] 10 | -------------------------------------------------------------------------------- /docker/rabbitmq/enabled_plugins: -------------------------------------------------------------------------------- 1 | [rabbitmq_management, rabbitmq_management_visualiser]. -------------------------------------------------------------------------------- /examples/server1/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /examples/server1/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /examples/server1/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master 6 | [travis-url]: https://travis-ci.org/nestjs/nest 7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux 8 | [linux-url]: https://travis-ci.org/nestjs/nest 9 | 10 |

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

11 |

12 | NPM Version 13 | Package License 14 | NPM Downloads 15 | Travis 16 | Linux 17 | Coverage 18 | Gitter 19 | Backers on Open Collective 20 | Sponsors on Open Collective 21 | 22 | 23 |

24 | 26 | 27 | ## Description 28 | 29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 30 | 31 | ## Installation 32 | 33 | ```bash 34 | $ npm install 35 | ``` 36 | 37 | ## Running the app 38 | 39 | ```bash 40 | # development 41 | $ npm run start 42 | 43 | # watch mode 44 | $ npm run start:dev 45 | 46 | # production mode 47 | $ npm run start:prod 48 | ``` 49 | 50 | ## Test 51 | 52 | ```bash 53 | # unit tests 54 | $ npm run test 55 | 56 | # e2e tests 57 | $ npm run test:e2e 58 | 59 | # test coverage 60 | $ npm run test:cov 61 | ``` 62 | 63 | ## Support 64 | 65 | 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). 66 | 67 | ## Stay in touch 68 | 69 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 70 | - Website - [https://nestjs.com](https://nestjs.com/) 71 | - Twitter - [@nestframework](https://twitter.com/nestframework) 72 | 73 | ## License 74 | 75 | Nest is [MIT licensed](LICENSE). 76 | -------------------------------------------------------------------------------- /examples/server1/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /examples/server1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server1", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "license": "MIT", 7 | "scripts": { 8 | "prebuild": "rimraf dist", 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "tslint -p tsconfig.json -c tslint.json", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@nestjs/common": "^6.10.11", 24 | "@nestjs/core": "^6.10.11", 25 | "@nestjs/platform-express": "^6.10.11", 26 | "amqp-connection-manager": "^3.1.0", 27 | "amqplib": "^0.5.5", 28 | "nestjs-transport-eventbus": "^1.0.24", 29 | "reflect-metadata": "^0.1.13", 30 | "rxjs": "^6.5.3" 31 | }, 32 | "devDependencies": { 33 | "@nestjs/cli": "^6.12.9", 34 | "@nestjs/schematics": "^6.7.6", 35 | "@nestjs/testing": "^6.10.11", 36 | "@types/express": "^4.17.2", 37 | "@types/jest": "^24.0.23", 38 | "@types/node": "^12.12.19", 39 | "@types/supertest": "^2.0.8", 40 | "jest": "^24.9.0", 41 | "prettier": "^1.19.1", 42 | "supertest": "^4.0.2", 43 | "ts-jest": "^24.2.0", 44 | "ts-loader": "^6.2.1", 45 | "rimraf": "^3.0.0", 46 | "ts-node": "^8.5.4", 47 | "tsconfig-paths": "^3.9.0", 48 | "tslint": "^5.20.1", 49 | "typescript": "^3.7.3" 50 | }, 51 | "jest": { 52 | "moduleFileExtensions": [ 53 | "js", 54 | "json", 55 | "ts" 56 | ], 57 | "rootDir": "src", 58 | "testRegex": ".spec.ts$", 59 | "transform": { 60 | "^.+\\.(t|j)s$": "ts-jest" 61 | }, 62 | "coverageDirectory": "../coverage", 63 | "testEnvironment": "node" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/server1/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /examples/server1/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) { 7 | } 8 | 9 | @Get('/default') 10 | handleDefaultEvent() { 11 | this.appService.handleDefaultEvent(); 12 | } 13 | 14 | @Get('/rabbit') 15 | rabbit() { 16 | this.appService.rabbitEvent(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/server1/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { TransportEventBusModule } from 'nestjs-transport-eventbus'; 5 | import { DefaultEventHandler } from './handlers/default.event.handler'; 6 | import { RabbitPublisher } from './publishers/rabbit.publisher'; 7 | 8 | @Module({ 9 | imports: [ 10 | TransportEventBusModule.forRoot({ 11 | publishers: [RabbitPublisher] 12 | }) 13 | ], 14 | controllers: [AppController], 15 | providers: [ 16 | AppService, 17 | DefaultEventHandler 18 | ], 19 | }) 20 | export class AppModule { 21 | } 22 | -------------------------------------------------------------------------------- /examples/server1/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | import { TRANSPORT_EVENT_BUS_SERVICE } from 'nestjs-transport-eventbus'; 3 | import { IEventBus } from '@nestjs/cqrs'; 4 | import { DefaultEvent } from './events/default.event'; 5 | import { RabbitEvent } from './events/rabbit.event'; 6 | 7 | @Injectable() 8 | export class AppService { 9 | constructor( 10 | @Inject(TRANSPORT_EVENT_BUS_SERVICE) private readonly eventBus: IEventBus 11 | ){ 12 | 13 | } 14 | handleDefaultEvent(): void { 15 | this.eventBus.publish(new DefaultEvent('Pass some param')); 16 | } 17 | rabbitEvent(): void { 18 | this.eventBus.publish(new RabbitEvent('Pass some param')); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/server1/src/events/default.event.ts: -------------------------------------------------------------------------------- 1 | 2 | export class DefaultEvent { 3 | constructor( 4 | readonly message: string 5 | ) { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/server1/src/events/rabbit.event.ts: -------------------------------------------------------------------------------- 1 | import { TransportType, ExcludeDef } from 'nestjs-transport-eventbus'; 2 | import { Transport } from '@nestjs/microservices'; 3 | 4 | @TransportType(Transport.RMQ) 5 | @ExcludeDef() 6 | export class RabbitEvent { 7 | constructor( 8 | readonly message: string 9 | ) { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/server1/src/handlers/default.event.handler.ts: -------------------------------------------------------------------------------- 1 | import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; 2 | import { DefaultEvent } from '../events/default.event'; 3 | 4 | @EventsHandler(DefaultEvent) 5 | export class DefaultEventHandler implements IEventHandler { 6 | 7 | handle(event: DefaultEvent) { 8 | console.info('handling event from DefaultEventHandler\n:', event); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/server1/src/handlers/rabbit.event.handler.ts: -------------------------------------------------------------------------------- 1 | import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; 2 | import { RabbitEvent } from '../events/rabbit.event'; 3 | 4 | @EventsHandler(RabbitEvent) 5 | export class RabbitEventHandler implements IEventHandler { 6 | handle(event: RabbitEvent) { 7 | console.debug('handling event from RabbitEventHandler:\n', event); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/server1/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | 4 | async function bootstrap() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3001, '0.0.0.0', () => { 7 | console.info(`Running server1: port: ${3001}`); 8 | }); 9 | } 10 | 11 | bootstrap(); 12 | -------------------------------------------------------------------------------- /examples/server1/src/publishers/rabbit.publisher.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { Publisher } from 'nestjs-transport-eventbus'; 3 | import { Client, ClientProxy, Transport } from '@nestjs/microservices'; 4 | 5 | @Injectable() 6 | @Publisher(Transport.RMQ) 7 | export class RabbitPublisher { 8 | @Client({ 9 | transport: Transport.RMQ, 10 | options: { 11 | urls: ['amqp://rabbit:rabbit@rabbitmq:5672'], 12 | queue: 'event_service_queue', 13 | queueOptions: { 14 | durable: true, 15 | }, 16 | }, 17 | }) 18 | client: ClientProxy; 19 | } 20 | -------------------------------------------------------------------------------- /examples/server1/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /examples/server1/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/server1/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/server1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /examples/server1/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "rules": { 8 | "quotemark": [true, "single"], 9 | "member-access": [false], 10 | "ordered-imports": [false], 11 | "max-line-length": [true, 150], 12 | "member-ordering": [false], 13 | "interface-name": [false], 14 | "arrow-parens": false, 15 | "object-literal-sort-keys": false 16 | }, 17 | "rulesDirectory": [] 18 | } 19 | -------------------------------------------------------------------------------- /examples/server2/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /examples/server2/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /examples/server2/README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master 6 | [travis-url]: https://travis-ci.org/nestjs/nest 7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux 8 | [linux-url]: https://travis-ci.org/nestjs/nest 9 | 10 |

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

11 |

12 | NPM Version 13 | Package License 14 | NPM Downloads 15 | Travis 16 | Linux 17 | Coverage 18 | Gitter 19 | Backers on Open Collective 20 | Sponsors on Open Collective 21 | 22 | 23 |

24 | 26 | 27 | ## Description 28 | 29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 30 | 31 | ## Installation 32 | 33 | ```bash 34 | $ npm install 35 | ``` 36 | 37 | ## Running the app 38 | 39 | ```bash 40 | # development 41 | $ npm run start 42 | 43 | # watch mode 44 | $ npm run start:dev 45 | 46 | # production mode 47 | $ npm run start:prod 48 | ``` 49 | 50 | ## Test 51 | 52 | ```bash 53 | # unit tests 54 | $ npm run test 55 | 56 | # e2e tests 57 | $ npm run test:e2e 58 | 59 | # test coverage 60 | $ npm run test:cov 61 | ``` 62 | 63 | ## Support 64 | 65 | 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). 66 | 67 | ## Stay in touch 68 | 69 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 70 | - Website - [https://nestjs.com](https://nestjs.com/) 71 | - Twitter - [@nestframework](https://twitter.com/nestframework) 72 | 73 | ## License 74 | 75 | Nest is [MIT licensed](LICENSE). 76 | -------------------------------------------------------------------------------- /examples/server2/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /examples/server2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server2", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "license": "MIT", 7 | "scripts": { 8 | "prebuild": "rimraf dist", 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "tslint -p tsconfig.json -c tslint.json", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@nestjs/common": "^6.10.11", 24 | "@nestjs/core": "^6.10.11", 25 | "@nestjs/platform-express": "^6.10.11", 26 | "amqp-connection-manager": "^3.1.0", 27 | "amqplib": "^0.5.5", 28 | "nestjs-transport-eventbus": "^1.0.24", 29 | "reflect-metadata": "^0.1.13", 30 | "rimraf": "^3.0.0", 31 | "rxjs": "^6.5.3" 32 | }, 33 | "devDependencies": { 34 | "@nestjs/cli": "^6.12.9", 35 | "@nestjs/schematics": "^6.7.6", 36 | "@nestjs/testing": "^6.10.11", 37 | "@types/express": "^4.17.2", 38 | "@types/jest": "^24.0.23", 39 | "@types/node": "^12.12.19", 40 | "@types/supertest": "^2.0.8", 41 | "jest": "^24.9.0", 42 | "prettier": "^1.19.1", 43 | "supertest": "^4.0.2", 44 | "ts-jest": "^24.2.0", 45 | "ts-loader": "^6.2.1", 46 | "ts-node": "^8.5.4", 47 | "tsconfig-paths": "^3.9.0", 48 | "tslint": "^5.20.1", 49 | "typescript": "^3.7.3" 50 | }, 51 | "jest": { 52 | "moduleFileExtensions": [ 53 | "js", 54 | "json", 55 | "ts" 56 | ], 57 | "rootDir": "src", 58 | "testRegex": ".spec.ts$", 59 | "transform": { 60 | "^.+\\.(t|j)s$": "ts-jest" 61 | }, 62 | "coverageDirectory": "../coverage", 63 | "testEnvironment": "node" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/server2/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /examples/server2/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | import { RabbitEventHandler } from './handlers/rabbit.event.handler'; 4 | import { TransportEventBusModule } from 'nestjs-transport-eventbus'; 5 | 6 | @Module({ 7 | imports: [ 8 | TransportEventBusModule 9 | ], 10 | controllers: [AppService], 11 | providers: [AppService, RabbitEventHandler], 12 | }) 13 | export class AppModule {} 14 | -------------------------------------------------------------------------------- /examples/server2/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Inject } from '@nestjs/common'; 2 | import { EventPattern } from '@nestjs/microservices'; 3 | import { TRANSPORT_EVENT_BUS_PATTERN, TRANSPORT_EVENT_BUS_SERVICE, TransportEvent } from 'nestjs-transport-eventbus'; 4 | import { IEvent, IEventBus } from '@nestjs/cqrs'; 5 | 6 | @Controller() 7 | export class AppService { 8 | 9 | constructor( 10 | @Inject(TRANSPORT_EVENT_BUS_SERVICE) private readonly eventBus: IEventBus 11 | ){ 12 | 13 | } 14 | @EventPattern(TRANSPORT_EVENT_BUS_PATTERN) 15 | accumulate(@TransportEvent() event: IEvent): void { 16 | this.eventBus.publish(event); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/server2/src/events/rabbit.event.ts: -------------------------------------------------------------------------------- 1 | export class RabbitEvent { 2 | constructor( 3 | readonly message: string 4 | ) { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/server2/src/handlers/rabbit.event.handler.ts: -------------------------------------------------------------------------------- 1 | import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; 2 | import { RabbitEvent } from '../events/rabbit.event'; 3 | 4 | @EventsHandler(RabbitEvent) 5 | export class RabbitEventHandler implements IEventHandler { 6 | 7 | handle(event: RabbitEvent) { 8 | console.debug('handling event from RabbitEventHandler:\n', event); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/server2/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { AppModule } from './app.module'; 3 | import { Transport } from '@nestjs/microservices'; 4 | import { Logger } from '@nestjs/common'; 5 | 6 | const log = new Logger('rabbit-service: '); 7 | 8 | (async function bootstrap() { 9 | const app = await NestFactory.createMicroservice(AppModule, { 10 | logger: log, 11 | transport: Transport.RMQ, 12 | options: { 13 | urls: ['amqp://rabbit:rabbit@rabbitmq:5672'], 14 | queue: 'event_service_queue', 15 | queueOptions: { 16 | durable: true, 17 | }, 18 | }, 19 | }); 20 | 21 | await app.listen(() => { 22 | log.verbose('rabbit service is running.'); 23 | }); 24 | })(); 25 | -------------------------------------------------------------------------------- /examples/server2/test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /examples/server2/test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/server2/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/server2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /examples/server2/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "rules": { 8 | "quotemark": [true, "single"], 9 | "member-access": [false], 10 | "ordered-imports": [false], 11 | "max-line-length": [true, 150], 12 | "member-ordering": [false], 13 | "interface-name": [false], 14 | "arrow-parens": false, 15 | "object-literal-sort-keys": false 16 | }, 17 | "rulesDirectory": [] 18 | } 19 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | export * from './dist'; 2 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "ts", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src" 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-transport-eventbus", 3 | "version": "1.0.24", 4 | "description": "Transport EventBus for NestJs", 5 | "author": "Sergey Telpuk", 6 | "license": "MIT", 7 | "scripts": { 8 | "build": "rimraf dist && tsc -p tsconfig.build.json && cp package.json dist/", 9 | "lint": "tslint -p tsconfig.json -c tslint.json", 10 | "lint:fix": "tslint -p tsconfig.json -c tslint.json --fix", 11 | "test:watch": "jest --watch --verbose --config ./test/jest.json", 12 | "test:cov": "jest --coverage", 13 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 14 | "test:e2e": "jest --verbose --config ./test/e2e/jest-e2e.json", 15 | "test:int": "jest --verbose --config ./test/int/jest-int.json", 16 | "test": "jest --verbose --config ./test/jest.json" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/sergey-telpuk/nestjs-transport-eventbus.git" 21 | }, 22 | "main": "./dist/index.js", 23 | "types": "./dist/index.d.ts", 24 | "dependencies": { 25 | "@nestjs/common": "^7.6.15", 26 | "@nestjs/core": "^7.6.15", 27 | "@nestjs/cqrs": "^7.0.1", 28 | "@nestjs/microservices": "^7.6.15", 29 | "@nestjs/platform-express": "^7.6.15", 30 | "reflect-metadata": "^0.1.13", 31 | "rxjs": "^6.6.7" 32 | }, 33 | "devDependencies": { 34 | "@nestjs/testing": "^7.6.15", 35 | "@types/express": "^4.17.11", 36 | "@types/jest": "^26.0.22", 37 | "@types/node": "^14.14.37", 38 | "@types/supertest": "^2.0.11", 39 | "amqp-connection-manager": "^3.2.2", 40 | "amqplib": "^0.7.1", 41 | "jest": "^26.6.3", 42 | "prettier": "^2.2.1", 43 | "rimraf": "^3.0.2", 44 | "supertest": "6.1.3", 45 | "ts-jest": "^26.5.4", 46 | "ts-node": "^9.1.1", 47 | "tsc-watch": "^4.2.9", 48 | "tsconfig-paths": "3.9.0", 49 | "tslint": "^5.20.1", 50 | "typescript": "^4.2.3" 51 | }, 52 | "jest": { 53 | "moduleFileExtensions": [ 54 | "js", 55 | "json", 56 | "ts" 57 | ], 58 | "rootDir": "src", 59 | "testRegex": ".spec.ts$", 60 | "transform": { 61 | "^.+\\.(t|j)s$": "ts-jest" 62 | }, 63 | "coverageDirectory": "../coverage", 64 | "testEnvironment": "node" 65 | }, 66 | "keywords": [ 67 | "eventbus", 68 | "microservices", 69 | "transport", 70 | "nestjs" 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /src/constants/transport.event-bus.constants.ts: -------------------------------------------------------------------------------- 1 | 2 | export const TRANSPORT_EVENT_BUS_PATTERN = process.env.TRANSPORT_EVENT_BUS_PATTERN || 'TRANSPORT_EVENT_BUS_PATTERN'; 3 | export const EXCLUDE_DEF = '__EXCLUDE_DEF'; 4 | export const TRANSPORTS = '__TRANSPORTS'; 5 | export const TRANSPORT = '__TRANSPORT'; 6 | export const EVENT_NAME = '__EVENT_NAME'; 7 | export const TRANSPORT_EVENT_BUS_SERVICE = Symbol('TransportEventBusService'); 8 | export const TRANSPORT_EVENT_BUS_PUBLISHER = Symbol('TransportEventBusPublisher'); 9 | -------------------------------------------------------------------------------- /src/decorators/transport.event.event-bus.decarator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator } from '@nestjs/common'; 2 | import { IEvent } from '@nestjs/cqrs'; 3 | import { ITransportDataEventBus } from '../interfaces/transport.data.event-bus.interface'; 4 | 5 | export const TransportEvent = createParamDecorator((_, data: any): IEvent => { 6 | if (!data) { 7 | return; 8 | } 9 | const object: ITransportDataEventBus = data.find((i: ITransportDataEventBus) => i.payload && i.eventName); 10 | if (!object) { 11 | return; 12 | } 13 | 14 | const anonymousClass = class {}; 15 | Object.defineProperty(anonymousClass, 'name', {value: object.eventName}); 16 | const newObject = new anonymousClass(); 17 | Object.keys(object.payload).forEach((prop) => { 18 | newObject[prop] = object.payload[prop]; 19 | }); 20 | 21 | return newObject; 22 | }); 23 | -------------------------------------------------------------------------------- /src/decorators/transport.exclude-def.decorator.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import { IEvent } from '@nestjs/cqrs'; 3 | import { EXCLUDE_DEF } from '../constants/transport.event-bus.constants'; 4 | 5 | export function ExcludeDef() { 6 | return {}>(constructor: T) => { 7 | const name = constructor.name; 8 | const object = class extends constructor implements IEvent { 9 | readonly [EXCLUDE_DEF] = true; 10 | }; 11 | 12 | Object.defineProperty(object, 'name', {value: name}); 13 | return object; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/decorators/transport.publisher.event-bus.decorator.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import { IEvent, IEventPublisher } from '@nestjs/cqrs'; 3 | import { EVENT_NAME, TRANSPORT, TRANSPORT_EVENT_BUS_PATTERN } from '../constants/transport.event-bus.constants'; 4 | import { ClientProxy } from '@nestjs/microservices'; 5 | import { ITransportPublisherEventBus } from '../interfaces/transport.publisher.event-bus.interface'; 6 | import { ITransportDataEventBus } from '../interfaces/transport.data.event-bus.interface'; 7 | import { Logger, LoggerService } from '@nestjs/common/services/logger.service'; 8 | import { Transport } from '@nestjs/microservices'; 9 | 10 | export function Publisher(TYPE: Transport) { 11 | return {}>(constructor: T) => { 12 | const name = constructor.name; 13 | const object = class extends constructor implements IEventPublisher, ITransportPublisherEventBus { 14 | readonly [TRANSPORT]: Transport = TYPE; 15 | readonly client: ClientProxy; 16 | readonly logger: LoggerService; 17 | 18 | public async publish(event: TPub): Promise { 19 | const data: ITransportDataEventBus = { 20 | payload: event, 21 | eventName: event[EVENT_NAME],// tslint:disable-line 22 | }; 23 | 24 | delete event[EVENT_NAME];// tslint:disable-line 25 | 26 | this.send(data); 27 | } 28 | 29 | public async send(data: ITransportDataEventBus) { 30 | try { 31 | await this.client.send(TRANSPORT_EVENT_BUS_PATTERN, data).toPromise(); 32 | } catch (err) { 33 | if (!this.logger) { 34 | (new Logger(ClientProxy.name)).error(err); 35 | return; 36 | } 37 | this.logger.error(err); 38 | } 39 | } 40 | }; 41 | 42 | Object.defineProperty(object, 'name', {value: name}); 43 | return object; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/decorators/transport.type.event-bus.decorator.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import { IEvent } from '@nestjs/cqrs'; 3 | import { Transport } from '@nestjs/microservices'; 4 | import { EVENT_NAME, TRANSPORTS } from '../constants/transport.event-bus.constants'; 5 | 6 | export function TransportType(...TYPE: Transport[]) { 7 | return {}>(constructor: T) => { 8 | const name = constructor.name; 9 | const object = class extends constructor implements IEvent { 10 | readonly [EVENT_NAME]: string = name; 11 | readonly [TRANSPORTS] = TYPE; 12 | }; 13 | Object.defineProperty(object, 'name', {value: name}); 14 | return object; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | export * from './transport.event-bus.module'; 3 | export * from './transport.event-bus.service'; 4 | export * from './transport.event-bus.publisher'; 5 | // constants 6 | export * from './constants/transport.event-bus.constants'; 7 | // interfaces 8 | export * from './interfaces/transport.data.event-bus.interface'; 9 | export * from './interfaces/transport.publisher.event-bus.interface'; 10 | // decorators 11 | export * from './decorators/transport.type.event-bus.decorator'; 12 | export * from './decorators/transport.exclude-def.decorator'; 13 | export * from './decorators/transport.publisher.event-bus.decorator'; 14 | export * from './decorators/transport.event.event-bus.decarator'; 15 | -------------------------------------------------------------------------------- /src/interfaces/transport.data.event-bus.interface.ts: -------------------------------------------------------------------------------- 1 | import { IEvent } from '@nestjs/cqrs'; 2 | 3 | export interface ITransportDataEventBus { 4 | payload: IEvent; 5 | eventName: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/interfaces/transport.publisher.event-bus.interface.ts: -------------------------------------------------------------------------------- 1 | import { ClientProxy } from '@nestjs/microservices'; 2 | import { Transport } from '@nestjs/microservices'; 3 | import { TRANSPORT } from '../constants/transport.event-bus.constants'; 4 | 5 | export interface ITransportPublisherEventBus { 6 | readonly [TRANSPORT]: Transport; 7 | readonly client: ClientProxy; 8 | } 9 | -------------------------------------------------------------------------------- /src/transport.event-bus.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Global, Module } from '@nestjs/common'; 2 | import { CqrsModule, EventBus } from '@nestjs/cqrs'; 3 | import { TransportEventBusService } from './transport.event-bus.service'; 4 | import { ModuleRef } from '@nestjs/core'; 5 | import { TransportEventBusPublisher } from './transport.event-bus.publisher'; 6 | import { TRANSPORT_EVENT_BUS_PUBLISHER, TRANSPORT_EVENT_BUS_SERVICE } from './constants/transport.event-bus.constants'; 7 | 8 | @Global() 9 | @Module({ 10 | imports: [ 11 | CqrsModule 12 | ], 13 | providers: [ 14 | { 15 | provide: TRANSPORT_EVENT_BUS_PUBLISHER, 16 | useClass: TransportEventBusPublisher 17 | }, 18 | { 19 | provide: TRANSPORT_EVENT_BUS_SERVICE, 20 | useFactory: (eventBus: EventBus, moduleRef: ModuleRef) => { 21 | return new TransportEventBusService( 22 | [], 23 | eventBus, 24 | moduleRef 25 | ); 26 | }, 27 | inject: [EventBus, ModuleRef], 28 | }, 29 | ], 30 | exports: [ 31 | TRANSPORT_EVENT_BUS_SERVICE, 32 | TRANSPORT_EVENT_BUS_PUBLISHER, 33 | ] 34 | }) 35 | export class TransportEventBusModule { 36 | static forRoot( 37 | { 38 | publishers = [], 39 | providers = [] 40 | } 41 | ): DynamicModule { 42 | 43 | return { 44 | module: TransportEventBusModule, 45 | imports: [ 46 | CqrsModule 47 | ], 48 | providers: [ 49 | ...publishers, 50 | ...providers, 51 | { 52 | provide: TRANSPORT_EVENT_BUS_PUBLISHER, 53 | useClass: TransportEventBusPublisher 54 | }, 55 | { 56 | provide: TRANSPORT_EVENT_BUS_SERVICE, 57 | useFactory: (eventBus: EventBus, moduleRef: ModuleRef) => { 58 | return new TransportEventBusService( 59 | publishers, 60 | eventBus, 61 | moduleRef 62 | ); 63 | }, 64 | inject: [EventBus, ModuleRef], 65 | }, 66 | ], 67 | exports: [ 68 | TRANSPORT_EVENT_BUS_SERVICE, 69 | TRANSPORT_EVENT_BUS_PUBLISHER, 70 | ...publishers 71 | ], 72 | }; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/transport.event-bus.publisher.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | import { EventBus, EventPublisher } from '@nestjs/cqrs'; 3 | import { TRANSPORT_EVENT_BUS_SERVICE } from './constants/transport.event-bus.constants'; 4 | 5 | @Injectable() 6 | export class TransportEventBusPublisher extends EventPublisher { 7 | constructor( 8 | @Inject(TRANSPORT_EVENT_BUS_SERVICE) private readonly iEventBus: EventBus 9 | ) { 10 | super(iEventBus); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/transport.event-bus.service.ts: -------------------------------------------------------------------------------- 1 | import { EventBus, EventHandlerType, IEvent, IEventBus, IEventHandler, IEventPublisher } from '@nestjs/cqrs'; 2 | import { Injectable, Type } from '@nestjs/common'; 3 | import { ModuleRef } from '@nestjs/core'; 4 | import { Transport } from '@nestjs/microservices'; 5 | import { EVENT_NAME, EXCLUDE_DEF, TRANSPORT, TRANSPORTS } from './constants/transport.event-bus.constants'; 6 | 7 | @Injectable() 8 | export class TransportEventBusService implements IEventBus { 9 | constructor( 10 | readonly publishers: any[], 11 | readonly eventBus: EventBus, 12 | private readonly moduleRef: ModuleRef 13 | ) { 14 | } 15 | 16 | get publisher(): IEventPublisher { 17 | return this.eventBus.publisher; 18 | } 19 | 20 | onModuleDestroy(): void { 21 | this.eventBus.onModuleDestroy(); 22 | } 23 | 24 | bind(handler: IEventHandler, name: string): void { 25 | this.eventBus.bind(handler, name); 26 | } 27 | 28 | registerSagas(types?: Array>): void { 29 | this.eventBus.registerSagas(types); 30 | } 31 | 32 | register(handlers?: EventHandlerType[]): void { 33 | this.eventBus.register(handlers); 34 | } 35 | 36 | publish(event: T): void { 37 | this.publishViaPublisher(event); 38 | } 39 | 40 | publishAll(events: T[]): void { 41 | events.forEach((event: T) => { 42 | this.publishViaPublisher(event); 43 | }); 44 | } 45 | 46 | private publishViaPublisher(event: T): void { 47 | const transports: Transport[] = event[TRANSPORTS] ? event[TRANSPORTS] : [];// tslint:disable-line 48 | const isExcludedDef: boolean = !!event[EXCLUDE_DEF] || false;// tslint:disable-line 49 | 50 | delete event[TRANSPORTS];// tslint:disable-line 51 | delete event[EXCLUDE_DEF];// tslint:disable-line 52 | 53 | for (const publisher of this.publishers) { 54 | 55 | const pub = this.moduleRef.get(publisher); 56 | 57 | if (transports.includes(pub[TRANSPORT])) {// tslint:disable-line 58 | pub.publish(event); 59 | } 60 | } 61 | 62 | if (!isExcludedDef) { 63 | delete event[EVENT_NAME];// tslint:disable-line 64 | this.eventBus.publish(event); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /test/int/jest-int.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "js", 4 | "json", 5 | "ts" 6 | ], 7 | "rootDir": ".", 8 | "testEnvironment": "node", 9 | "testRegex": ".int.test.ts$", 10 | "transform": { 11 | "^.+\\.(t|j)s$": "ts-jest" 12 | } 13 | } -------------------------------------------------------------------------------- /test/int/transport-eventbus/commands/try.aggregate-root.command.ts: -------------------------------------------------------------------------------- 1 | export class TryAggregateRootCommand { 2 | constructor( 3 | public readonly message: string, 4 | ) {} 5 | } 6 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/commands/try.saga.command.ts: -------------------------------------------------------------------------------- 1 | export class TrySagaCommand { 2 | constructor( 3 | public readonly message: string, 4 | ) {} 5 | } 6 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/eventbus.int.test.ts: -------------------------------------------------------------------------------- 1 | import { INestApplication, Logger } from '@nestjs/common'; 2 | import { Test } from '@nestjs/testing'; 3 | import { TransportEventBusModule } from '../../../src/transport.event-bus.module'; 4 | import { RabbitPublisher, TestClientProxy } from './publishers/rabbit.publisher'; 5 | import { 6 | DefaultEvent, 7 | ExcludeDefEvent, 8 | RabbitEvent, 9 | RabbitWithDefEvent, 10 | RabbitWithoutDefEvent, SagaEvent, 11 | } from './events/test.events'; 12 | import { DefaultEventHandler, RabbitWithDefEventHandler } from './handlers/default.event.handler'; 13 | import { Storage } from './storage/storage'; 14 | import { TrySagaCommandHandler } from './handlers/try.saga.command.handler'; 15 | import { TrySagaHandler } from './handlers/try.saga.handler'; 16 | import { TestEventService } from './service/test.service'; 17 | import { CommandBus, CqrsModule, IEventBus } from '@nestjs/cqrs'; 18 | import { TryAggregateRootCommand } from './commands/try.aggregate-root.command'; 19 | import { TryAggregateRootCommandHandler } from './handlers/try.aggregate-root.command.handler'; 20 | import { SagaEventHandler, TryAggregateRootEventHandler } from './handlers/try.aggregate-root.event.handler'; 21 | import { TRANSPORT_EVENT_BUS_SERVICE } from '../../../src/constants/transport.event-bus.constants'; 22 | 23 | describe('Transport EventBus service', () => { 24 | let app: INestApplication; 25 | let service: IEventBus; 26 | let storage: Storage; 27 | let testEventService: TestEventService; 28 | let commandBus: CommandBus; 29 | 30 | beforeEach(() => { 31 | storage.clear(); 32 | }); 33 | 34 | beforeAll(async () => { 35 | const moduleFixture = await Test.createTestingModule( 36 | { 37 | imports: [ 38 | CqrsModule, 39 | TransportEventBusModule.forRoot( 40 | { 41 | publishers: [RabbitPublisher], 42 | providers: [Storage, Logger, TestClientProxy] 43 | } 44 | ) 45 | ], 46 | providers: [ 47 | Storage, 48 | TestEventService, 49 | // //events handlers 50 | DefaultEventHandler, 51 | RabbitWithDefEventHandler, 52 | TryAggregateRootEventHandler, 53 | TrySagaCommandHandler, 54 | TrySagaHandler, 55 | // //command handlers 56 | TryAggregateRootCommandHandler, 57 | SagaEventHandler 58 | 59 | ], 60 | controllers: [], 61 | }, 62 | ).compile(); 63 | 64 | app = moduleFixture.createNestApplication(); 65 | service = moduleFixture.get(TRANSPORT_EVENT_BUS_SERVICE); 66 | storage = moduleFixture.get(Storage); 67 | testEventService = moduleFixture.get(TestEventService); 68 | commandBus = moduleFixture.get(CommandBus); 69 | 70 | await app.init(); 71 | }); 72 | 73 | describe('Transport Events', () => { 74 | it('should call a DefaultEvent handler', () => { 75 | service.publish(new DefaultEvent('DefaultEvent')); 76 | expect(storage.get('DefaultEvent')).toEqual('DefaultEvent'); // success 77 | }); 78 | 79 | it('shouldn\'t call a ExcludeDefHandlerEvent handler', () => { 80 | service.publish(new ExcludeDefEvent('ExcludeDefEvent')); 81 | expect(storage.get('ExcludeDefEvent')).toEqual(false); // success 82 | }); 83 | 84 | it('should call a RabbitWithDefEvent handler', () => { 85 | service.publish(new RabbitWithDefEvent('RabbitWithDefEvent')); 86 | expect(storage.get('Rabbit')).toEqual('RabbitWithDefEvent'); // success 87 | expect(storage.get('RabbitWithDefEvent')).toEqual('RabbitWithDefEvent'); // success 88 | }); 89 | 90 | it('should call a RabbitAndDefEvent handler', () => { 91 | service.publish(new RabbitWithoutDefEvent('RabbitWithoutDefEvent')); 92 | expect(storage.get('Rabbit')).toEqual('RabbitWithoutDefEvent'); // success 93 | expect(storage.get('DefaultEvent')).toEqual(false); // success 94 | }); 95 | 96 | it('should call both DefaultEvent and RabbitEvent handler', () => { 97 | service.publishAll([ 98 | new DefaultEvent('DefaultEvent'), 99 | new RabbitEvent('RabbitEvent') 100 | ]); 101 | expect(storage.get('DefaultEvent')).toEqual('DefaultEvent'); // success 102 | expect(storage.get('Rabbit')).toEqual('RabbitEvent'); // success 103 | }); 104 | }); 105 | 106 | describe('Transport Events with Saga', () => { 107 | it('should call SagaEvent', () => { 108 | service.publish(new SagaEvent('SagaEvent')); 109 | expect(storage.get('TrySagaCommand')).toEqual('SagaEvent'); // success 110 | }); 111 | }); 112 | 113 | describe('Service di', () => { 114 | it('should call RabbitEvent', () => { 115 | testEventService.publishEvent(new RabbitWithDefEvent('RabbitWithDefEvent')); 116 | expect(storage.get('RabbitWithDefEvent')).toEqual('RabbitWithDefEvent'); // success 117 | }); 118 | }); 119 | 120 | 121 | describe('Transport Events with AggregateRoot', () => { 122 | it('should call', () => { 123 | commandBus.execute(new TryAggregateRootCommand('TryAggregateRootEvent')); 124 | expect(storage.get('TryAggregateRootEvent')).toEqual('TryAggregateRootEvent'); // success 125 | }); 126 | }); 127 | 128 | afterAll(async () => { 129 | await app.close(); 130 | storage.clear(); 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/events/test.events.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:max-classes-per-file */ 2 | import { Transport } from '@nestjs/microservices'; 3 | import { ExcludeDef } from '../../../../src/decorators/transport.exclude-def.decorator'; 4 | import { TransportType } from '../../../../src/decorators/transport.type.event-bus.decorator'; 5 | 6 | @TransportType() 7 | export class DefaultEvent { 8 | constructor( 9 | readonly message: string 10 | ) { 11 | } 12 | } 13 | 14 | @TransportType() 15 | @ExcludeDef() 16 | export class ExcludeDefEvent { 17 | constructor( 18 | readonly message: string 19 | ) { 20 | } 21 | } 22 | 23 | @TransportType(Transport.RMQ) 24 | export class RabbitWithDefEvent { 25 | constructor( 26 | readonly message: string 27 | ) { 28 | } 29 | } 30 | 31 | @TransportType(Transport.RMQ) 32 | @ExcludeDef() 33 | export class RabbitEvent { 34 | constructor( 35 | readonly message: string 36 | ) { 37 | } 38 | } 39 | 40 | @TransportType(Transport.RMQ) 41 | @ExcludeDef() 42 | export class RabbitWithoutDefEvent { 43 | constructor( 44 | readonly message: string 45 | ) { 46 | } 47 | } 48 | 49 | @TransportType(Transport.RMQ) 50 | export class SagaEvent { 51 | constructor( 52 | readonly message: string 53 | ) { 54 | } 55 | } 56 | 57 | 58 | @TransportType(Transport.RMQ) 59 | export class TryAggregateRootEvent { 60 | constructor( 61 | readonly message: string 62 | ) { 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/handlers/default.event.handler.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:max-classes-per-file */ 2 | import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; 3 | import { DefaultEvent, RabbitWithDefEvent } from '../events/test.events'; 4 | import { Storage } from '../storage/storage'; 5 | 6 | @EventsHandler(DefaultEvent) 7 | export class DefaultEventHandler implements IEventHandler { 8 | constructor( 9 | private readonly storage: Storage 10 | ) { 11 | } 12 | 13 | handle(event: DefaultEvent) { 14 | this.storage.upsert('DefaultEvent', event.message); 15 | } 16 | } 17 | 18 | @EventsHandler(RabbitWithDefEvent) 19 | export class RabbitWithDefEventHandler implements IEventHandler { 20 | constructor( 21 | private readonly storage: Storage 22 | ) { 23 | } 24 | 25 | handle(event: RabbitWithDefEvent) { 26 | this.storage.upsert('RabbitWithDefEvent', event.message); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/handlers/try.aggregate-root.command.handler.ts: -------------------------------------------------------------------------------- 1 | import { CommandHandler, EventPublisher, ICommandHandler } from '@nestjs/cqrs'; 2 | import { TryAggregateRootCommand } from '../commands/try.aggregate-root.command'; 3 | import { Inject } from '@nestjs/common'; 4 | import { TRANSPORT_EVENT_BUS_PUBLISHER } from '../../../../src/constants/transport.event-bus.constants'; 5 | import { TestModel } from '../model/test.model'; 6 | 7 | @CommandHandler(TryAggregateRootCommand) 8 | export class TryAggregateRootCommandHandler implements ICommandHandler { 9 | constructor( 10 | @Inject(TRANSPORT_EVENT_BUS_PUBLISHER) private readonly publisher: EventPublisher 11 | ) { 12 | } 13 | 14 | async execute(command: TryAggregateRootCommand) { 15 | const {message} = command; 16 | const aggregator = this.publisher.mergeObjectContext( 17 | new TestModel() 18 | ); 19 | aggregator.applyEvent(message); 20 | aggregator.commit(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/handlers/try.aggregate-root.event.handler.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:max-classes-per-file */ 2 | import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; 3 | import { Storage } from '../storage/storage'; 4 | import { SagaEvent, TryAggregateRootEvent } from '../events/test.events'; 5 | 6 | @EventsHandler(TryAggregateRootEvent) 7 | export class TryAggregateRootEventHandler implements IEventHandler { 8 | constructor( 9 | private readonly storage: Storage 10 | ) { 11 | } 12 | 13 | handle(event: TryAggregateRootEvent) { 14 | this.storage.upsert('TryAggregateRootEvent', event.message); 15 | } 16 | } 17 | 18 | @EventsHandler(SagaEvent) 19 | export class SagaEventHandler implements IEventHandler { 20 | constructor( 21 | private readonly storage: Storage 22 | ) { 23 | } 24 | 25 | handle(event: SagaEvent) { 26 | this.storage.upsert('SagaEventHandler', event.message); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/handlers/try.saga.command.handler.ts: -------------------------------------------------------------------------------- 1 | import { TrySagaCommand } from '../commands/try.saga.command'; 2 | import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; 3 | import { Storage } from '../storage/storage'; 4 | 5 | @CommandHandler(TrySagaCommand) 6 | export class TrySagaCommandHandler implements ICommandHandler { 7 | constructor( 8 | private readonly storage: Storage 9 | ) { 10 | } 11 | 12 | async execute(command: TrySagaCommand) { 13 | this.storage.upsert('TrySagaCommand', command.message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/handlers/try.saga.handler.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { Observable } from 'rxjs'; 3 | import { ICommand, ofType, Saga } from '@nestjs/cqrs'; 4 | import { map } from 'rxjs/operators'; 5 | import { SagaEvent } from '../events/test.events'; 6 | import { TrySagaCommand } from '../commands/try.saga.command'; 7 | 8 | @Injectable() 9 | export class TrySagaHandler { 10 | @Saga() 11 | dragonKilled = (events$: Observable): Observable => { 12 | return events$.pipe( 13 | ofType(SagaEvent), 14 | map((event: SagaEvent) => { 15 | return new TrySagaCommand(event.message); 16 | }), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/model/test.model.ts: -------------------------------------------------------------------------------- 1 | import { AggregateRoot } from '@nestjs/cqrs'; 2 | import { TryAggregateRootEvent } from '../events/test.events'; 3 | 4 | export class TestModel extends AggregateRoot { 5 | constructor() { 6 | super(); 7 | } 8 | applyEvent(message: string) { 9 | this.apply(new TryAggregateRootEvent(message)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/publishers/rabbit.publisher.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:max-classes-per-file */ 2 | import { Injectable } from '@nestjs/common'; 3 | import { ClientProxy, ReadPacket, Transport, WritePacket } from '@nestjs/microservices'; 4 | import { Publisher } from '../../../../src/decorators/transport.publisher.event-bus.decorator'; 5 | import { defer, Observable, } from 'rxjs'; 6 | import { Storage } from '../storage/storage'; 7 | 8 | @Injectable() 9 | export class TestClientProxy extends ClientProxy { 10 | constructor( 11 | private storage: Storage 12 | ) { 13 | super(); 14 | } 15 | 16 | send(pattern: any, data: TInput): Observable { 17 | this.storage.upsert('Rabbit', data['payload']['message']);// tslint:disable-line 18 | return defer(() => Promise.resolve()); 19 | } 20 | 21 | close(): any { 22 | return undefined; 23 | } 24 | 25 | connect(): Promise { 26 | return undefined; 27 | } 28 | 29 | protected dispatchEvent(packet: ReadPacket): Promise { 30 | return undefined; 31 | } 32 | 33 | protected publish(packet: ReadPacket, callback: (packet: WritePacket) => void): any { 34 | return undefined; 35 | } 36 | } 37 | 38 | @Injectable() 39 | @Publisher(Transport.RMQ) 40 | export class RabbitPublisher { 41 | constructor( 42 | private client: TestClientProxy 43 | ) { 44 | // TODO 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/service/test.service.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from '@nestjs/common'; 2 | import { IEventBus } from '@nestjs/cqrs'; 3 | import { TRANSPORT_EVENT_BUS_SERVICE } from '../../../../src/constants/transport.event-bus.constants'; 4 | 5 | @Injectable() 6 | export class TestEventService { 7 | constructor( 8 | @Inject(TRANSPORT_EVENT_BUS_SERVICE) private readonly eventBus: IEventBus 9 | ) { 10 | // TODO 11 | } 12 | 13 | publishEvent(event: any) { 14 | this.eventBus.publish(event); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/int/transport-eventbus/storage/storage.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class Storage { 5 | private static data = {}; 6 | 7 | upsert(key: string, value: any) { 8 | Storage.data[key] = value; 9 | } 10 | 11 | get(key: string): any { 12 | return Storage.data[key] || false ; 13 | } 14 | 15 | getAll(): any { 16 | return Storage.data; 17 | } 18 | 19 | delete(key) { 20 | delete Storage.data[key]; 21 | } 22 | 23 | clear() { 24 | Storage.data = {}; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": [ 3 | "js", 4 | "json", 5 | "ts" 6 | ], 7 | "rootDir": ".", 8 | "testEnvironment": "node", 9 | "testRegex": ".test.ts$", 10 | "transform": { 11 | "^.+\\.(t|j)s$": "ts-jest" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./dist" 5 | }, 6 | "include": [ 7 | "src/*" 8 | ], 9 | "exclude": [ 10 | "node_modules", 11 | "**/*.spec.ts" 12 | ] 13 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "dist" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "quotemark": [ 9 | true, 10 | "single", 11 | "avoid-escape" 12 | ], 13 | "no-console": [ 14 | true, 15 | "log" 16 | ], 17 | "arrow-parens": false, 18 | "no-consecutive-blank-lines": [ 19 | true, 20 | 2 21 | ], 22 | "member-access": [ 23 | false 24 | ], 25 | "ordered-imports": [ 26 | false 27 | ], 28 | "object-literal-sort-keys": [ 29 | false 30 | ], 31 | "trailing-comma": [ 32 | false 33 | ], 34 | "semicolon": [ 35 | true, 36 | "always", 37 | "ignore-bound-class-methods" 38 | ], 39 | "max-line-length": [ 40 | false 41 | ], 42 | "one-line": [ 43 | true, 44 | "check-catch", 45 | "check-finally", 46 | "check-else", 47 | "check-open-brace" 48 | ], 49 | "whitespace": [ 50 | true, 51 | "check-decl", 52 | "check-operator", 53 | "check-module", 54 | "check-type", 55 | "check-typecast" 56 | ], 57 | "interface-name": false, 58 | "member-ordering": false, 59 | "object-literal-key-quotes": [ 60 | true, 61 | "as-needed" 62 | ] 63 | }, 64 | "linterOptions": { 65 | "exclude": [ 66 | "node_modules", 67 | "dist" 68 | ] 69 | }, 70 | "rulesDirectory": [] 71 | } 72 | --------------------------------------------------------------------------------