Skip to content

Commit

Permalink
feat!: adds capability to add multiple amq connections
Browse files Browse the repository at this point in the history
feat(listen): add connection naming to listener metadata
feat(listen-options): add null validation exception flag
test: add test cases to handle multiple connections
chore(examples): add example of multiple queue usage
chore(examples): update dependency

BREAKING CHANGE: `acceptValidationNullObjectException` is moved to `ListenOptions` and
will not be available on `QueueModuleOptions`.
  • Loading branch information
raschan committed Aug 24, 2021
1 parent a40cb62 commit af4d5bb
Show file tree
Hide file tree
Showing 52 changed files with 1,302 additions and 240 deletions.
2 changes: 1 addition & 1 deletion examples/01-Basic_Connection/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ services:
ARTEMIS_PASSWORD: secret
ports:
- "8161:8161"
- "61616:61616"
- "6161:61616"
- "5672:5672"
2 changes: 1 addition & 1 deletion examples/01-Basic_Connection/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"@nestjs/common": "^8.0.6",
"@nestjs/core": "^8.0.6",
"@nestjs/platform-express": "^8.0.6",
"@team-supercharge/nest-amqp": "^2.0.0",
"@team-supercharge/nest-amqp": "^3.0.0",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
"reflect-metadata": "^0.1.13",
Expand Down
20 changes: 20 additions & 0 deletions examples/02-Multiple_Connections/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Example for nest-amqp

This is a example NestJS project that is using nest-amqp with 2 separate brokers.
It is a REST-Interface to interact with an ActiveMQ Broker.

## Step 1: Start docker image to have an AMQP broker

```
docker-compose up
```

## Step 2: Start example

```
npm install
npm run start
```

HTTP server is listing under port `4444`.
You can use the requests in `./http-requests/add-user.http` for testing;
28 changes: 28 additions & 0 deletions examples/02-Multiple_Connections/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
version: "3.7"

services:
activemq-artemis-1:
image: vromero/activemq-artemis:2.15.0
container_name: activemq-artemis-1
hostname: activemq-artemis-1
restart: "no"
environment:
ARTEMIS_USERNAME: artemis1
ARTEMIS_PASSWORD: secret
ports:
- "18161:8161"
- "61616:61616"
- "15672:5672"

activemq-artemis-2:
image: vromero/activemq-artemis:2.15.0
container_name: activemq-artemis-2
hostname: activemq-artemis-2
restart: "no"
environment:
ARTEMIS_USERNAME: artemis2
ARTEMIS_PASSWORD: secret
ports:
- "28161:8161"
- "62626:61616"
- "25672:5672"
19 changes: 19 additions & 0 deletions examples/02-Multiple_Connections/http-requests/add-user.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// add user through connection A
POST http://{{host}}/user/connection-a
Content-Type: application/json

{
"name": "Peter",
"age": 23
}

###

// add user through connection B
POST http://{{host}}/user/connection-b
Content-Type: application/json

{
"name": "Eva",
"age": 20
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"development": {
"host": "localhost:4444"
}
}
4 changes: 4 additions & 0 deletions examples/02-Multiple_Connections/nest-cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"collection": "@nestjs/schematics",
"sourceRoot": "src"
}
31 changes: 31 additions & 0 deletions examples/02-Multiple_Connections/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "nest-amqp-example",
"scripts": {
"prebuild": "rimraf dist",
"build": "nest build",
"start": "nest start",
"start:dev": "nest start --watch",
"start:prod": "node dist/main"
},
"dependencies": {
"@nestjs/common": "^8.0.6",
"@nestjs/core": "^8.0.6",
"@nestjs/platform-express": "^8.0.6",
"@team-supercharge/nest-amqp": "^3.0.0",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.3.0"
},
"devDependencies": {
"@nestjs/cli": "^8.1.1",
"@nestjs/schematics": "^8.0.2",
"@types/express": "^4.17.13",
"@types/node": "^16.4.13",
"ts-loader": "^9.2.5",
"ts-node": "^10.2.0",
"tsconfig-paths": "^3.10.1",
"typescript": "^4.3.5"
}
}
9 changes: 9 additions & 0 deletions examples/02-Multiple_Connections/src/app/app.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Controller, Get } from '@nestjs/common';

@Controller()
export class AppController {
@Get()
public index(): string {
return 'The app is running...';
}
}
22 changes: 22 additions & 0 deletions examples/02-Multiple_Connections/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Module } from '@nestjs/common';
import { QueueModule } from '@team-supercharge/nest-amqp';

import { AppController } from './app.controller';
import { UserModule } from '../modules/user/user.module';

import { ConnectionName, CONNECTION_A_URI, CONNECTION_B_URI } from '../constant';

@Module({
imports: [
QueueModule.forRoot(
[
{ connectionUri: CONNECTION_A_URI, name: ConnectionName.A },
{ connectionUri: CONNECTION_B_URI, name: ConnectionName.B },
],
{},
),
UserModule,
],
controllers: [AppController],
})
export class AppModule {}
7 changes: 7 additions & 0 deletions examples/02-Multiple_Connections/src/constant/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export enum ConnectionName {
A = 'A',
B = 'B',
}

export const CONNECTION_A_URI = 'amqp://artemis1:secret@localhost:15672';
export const CONNECTION_B_URI = 'amqp://artemis2:secret@localhost:25672';
41 changes: 41 additions & 0 deletions examples/02-Multiple_Connections/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';

import { AppModule } from './app/app.module';

let app: INestApplication;

async function bootstrap() {
app = await NestFactory.create(AppModule);
await app.listen(4444);
}
bootstrap().catch(err => {
console.error('Error during the bootstrap:', err);
});

async function gracefulShutdown(_signal: string): Promise<void> {
try {
if (app) {
await app.close();
}

process.exit(0);
} catch (error) {
console.error(error);

process.exit(1);
}
}

process.once('SIGTERM', async () => {
await gracefulShutdown('SIGTERM');
});
process.once('SIGINT', async () => {
await gracefulShutdown('SIGINT');
});
process.once('SIGUSR1', async () => {
await gracefulShutdown('SIGUSR1');
});
process.once('SIGUSR2', async () => {
await gracefulShutdown('SIGUSR2');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Body, Controller, Post } from '@nestjs/common';
import { QueueService } from '@team-supercharge/nest-amqp';

import { UserQueue } from './user.queue';
import { UserDto } from './user.dto';
import { ConnectionName } from '../../constant';

@Controller('user')
export class UserController {
constructor(private readonly queueService: QueueService) {}

@Post('connection-a')
public async sendAddUserMessageOnA(@Body() body: UserDto): Promise<string> {
await this.queueService.send<UserDto>(UserQueue.ADD_USER, body, ConnectionName.A);

return 'Add user event sent';
}

@Post('connection-b')
public async sendAddUserMessageOnB(@Body() body: UserDto): Promise<string> {
await this.queueService.send<UserDto>(UserQueue.ADD_USER, body, ConnectionName.B);

return 'Add user event sent';
}
}
15 changes: 15 additions & 0 deletions examples/02-Multiple_Connections/src/modules/user/user.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Expose } from 'class-transformer';
import { IsInt, IsString } from 'class-validator';

@Expose()
export class UserDto {
@IsString()
public readonly name: string;

@IsInt()
public readonly age: number;

constructor(userData: UserDto) {
Object.assign(this, userData);
}
}
20 changes: 20 additions & 0 deletions examples/02-Multiple_Connections/src/modules/user/user.listener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Injectable, Logger } from '@nestjs/common';
import { Listen } from '@team-supercharge/nest-amqp';

import { UserQueue } from './user.queue';
import { UserDto } from './user.dto';
import { ConnectionName } from '../../constant';

@Injectable()
export class UserListener {
@Listen(UserQueue.ADD_USER, { type: UserDto }, ConnectionName.A)
public async listenOnAForAddUser(userData: UserDto): Promise<void> {
logger.log(`add new user: ${JSON.stringify(userData)} on connection ${ConnectionName.A}`);
}

@Listen(UserQueue.ADD_USER, { type: UserDto }, ConnectionName.B)
public async listenOnBForAddUser(userData: UserDto): Promise<void> {
logger.log(`add new user: ${JSON.stringify(userData)} on connection ${ConnectionName.B}`);
}
}
const logger = new Logger(UserListener.name);
12 changes: 12 additions & 0 deletions examples/02-Multiple_Connections/src/modules/user/user.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { QueueModule } from '@team-supercharge/nest-amqp';

import { UserController } from './user.controller';
import { UserListener } from './user.listener';

@Module({
controllers: [UserController],
providers: [UserListener],
imports: [QueueModule.forFeature()],
})
export class UserModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class UserQueue {
static get ADD_USER(): string {
return 'add-user';
}
}
4 changes: 4 additions & 0 deletions examples/02-Multiple_Connections/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
15 changes: 15 additions & 0 deletions examples/02-Multiple_Connections/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true
}
}
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@team-supercharge/nest-amqp",
"version": "2.3.0",
"version": "3.0.0",
"description": "AMQP 1.0 module for Nest framework",
"author": "Supercharge",
"license": "MIT",
Expand All @@ -16,6 +16,7 @@
"publishConfig": {
"access": "public"
},
"main": "dist/index.js",
"scripts": {
"prebuild": "rimraf dist",
"prebuild:prod": "rimraf dist",
Expand Down
5 changes: 5 additions & 0 deletions src/constant/amq.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const AMQP_CONNECTION_RECONNECT = 'AMQP_CONNECTION_RECONNECT';

export const AMQP_CLIENT_TOKEN = 'AMQP_CLIENT';
export const AMQP_DEFAULT_CONNECTION_TOKEN = 'AMQP_DEFAULT_CONNECTION';
export const AMQP_CONNECTION_OPTIONS_TOKEN = 'AMQP_CONNECTION_OPTIONS';
3 changes: 2 additions & 1 deletion src/constant/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './queue.constant';
export * from './queue-module.constant';
export * from './amq.constant';
2 changes: 2 additions & 0 deletions src/constant/queue-module.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const QUEUE_LISTEN_METADATA_KEY = 'queue-listener';
export const QUEUE_MODULE_OPTIONS = 'QUEUE_MODULE_OPTIONS';
4 changes: 0 additions & 4 deletions src/constant/queue.constant.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/decorator/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './listen.decorator';
export * from './listen/listen.decorator';
export * from './inject-amq-connection/inject-amq-connection.decorator';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Connection } from 'rhea-promise';
import { AMQP_CLIENT_TOKEN } from '../../constant';
import { InjectAMQConnection } from './inject-amq-connection.decorator';

describe('@InjectAMQConnection', () => {
const test = 'testConnection';

class Test {
constructor(
@InjectAMQConnection()
public readonly defaultConnection: Connection,

@InjectAMQConnection(test)
public readonly namedConnection: Connection,
) {}
}

it('should add default and named connection', () => {
const metadata = Reflect.getMetadata('self:paramtypes', Test);

const expectedMetadata = [
{ index: 1, param: `${test}_${AMQP_CLIENT_TOKEN}` },
{ index: 0, param: AMQP_CLIENT_TOKEN },
];
expect(metadata).toEqual(expectedMetadata);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Inject } from '@nestjs/common';
import { getAMQConnectionToken } from '../../util';

export const InjectAMQConnection = (connection?: string): ParameterDecorator => Inject(getAMQConnectionToken(connection));
Loading

0 comments on commit af4d5bb

Please sign in to comment.