Skip to content

Commit

Permalink
Merge branch 'master' into bugfix-provider-multi-export
Browse files Browse the repository at this point in the history
  • Loading branch information
AlariCode authored Nov 22, 2021
2 parents 44b3e06 + d324b5c commit 384e386
Show file tree
Hide file tree
Showing 33 changed files with 3,681 additions and 3,831 deletions.
45 changes: 45 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
"no-empty-function": "off",
"prettier/prettier": ['error', {
'singleQuote': true,
'useTabs': true,
'semi': true,
'trailingComma': 'all',
'bracketSpacing': true,
'printWidth': 100,
'endOfLine': 'auto'
}],
"@typescript-eslint/no-empty-function": ["off"],
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-namespace': 'off',
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
},
],
},

};
8 changes: 8 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
# Change log

## 2.7.0

- Added autoBindingRoutes optiom

## 2.6.2

- Update dependencies

## 2.6.1

- Added nack for ERROR_NO_ROUTE

## 2.6.0

- Updated dependencies
- Migrated tslint to eslint
- Added @RMQTransform() decorator to transform incomming message
- Changed @Validate() decorator to @RMQValidate(). @Validate() will be depricated.

## 2.5.1

- Updated RQMColorLogger performance and changed debug message content.

## 2.5.0

- Added support for random queue name and additional queueOprions.
- Added deprecation warning.
- Removed deprecated @RMQController

## 2.4.0

- Added TLS/SSL support

## 2.3.1

- Fixed import
Expand Down
92 changes: 81 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ In forRoot() you pass connection options:

Additionally, you can use optional parameters:

- **queueName** (string) - Queue name which your microservice would listen and bind topics specified in '@RMQRoute' decorator to this queue. If this parameter is not specified, your microservice could send messages and listen to reply or send notifications, but it couldn't get messages or notifications from other services.
- **queueName** (string) - Queue name which your microservice would listen and bind topics specified in '@RMQRoute' decorator to this queue. If this parameter is not specified, your microservice could send messages and listen to reply or send notifications, but it couldn't get messages or notifications from other services. If you use empty string, RabbitMQ will generate name for you.
Example:

```typescript
Expand All @@ -75,21 +75,24 @@ Additionally, you can use optional parameters:
queueName: 'my-service-queue',
}
```

- **prefetchCount** (boolean) - You can read more [here](https://github.com/postwait/node-amqp).
- **isGlobalPrefetchCount** (boolean) - You can read more [here](https://github.com/postwait/node-amqp).
- **connectionOptions** (object) - Additional connection options. You can read more [here](http://www.squaremobius.net/amqp.node/).
- **prefetchCount** (boolean) - You can read more [here](http://www.squaremobius.net/amqp.node/).
- **isGlobalPrefetchCount** (boolean) - You can read more [here](http://www.squaremobius.net/amqp.node/).
- **queueOptions** (object) - options for created queue.
- **reconnectTimeInSeconds** (number) - Time in seconds before reconnection retry. Default is 5 seconds.
- **heartbeatIntervalInSeconds** (number) - Interval to send heartbeats to broker. Defaults to 5 seconds.
- **queueArguments** (object) - You can read more about queue parameters [here](https://www.rabbitmq.com/parameters.html).
- **queueArguments** (!!! deprecated. Use queueOptions instead) - You can read more about queue parameters [here](https://www.rabbitmq.com/parameters.html).
- **messagesTimeout** (number) - Number of milliseconds 'post' method will wait for the response before a timeout error. Default is 30 000.
- **isQueueDurable** (boolean) - Makes created queue durable. Default is true.
- **isExchangeDurable** (boolean) - Makes created exchange durable. Default is true.
- **isQueueDurable** (!!! deprecated. Use queueOptions instead) - Makes created queue durable. Default is true.
- **isExchangeDurable** (!!! deprecated. Use exchangeOptions instead) - Makes created exchange durable. Default is true.
- **exchangeOptions** (Options.AssertExchange) - You can read more about exchange options [here](squaremobius.net/amqp.node/channel_api.html#channel_assertExchange).
- **logMessages** (boolean) - Enable printing all sent and recieved messages in console with its route and content. Default is false.
- **logger** (LoggerService) - Your custom logger service that implements `LoggerService` interface. Compatible with Winston and other loggers.
- **middleware** (array) - Array of middleware functions that extends `RMQPipeClass` with one method `transform`. They will be triggered right after recieving message, before pipes and controller method. Trigger order is equal to array order.
- **errorHandler** (class) - custom error handler for dealing with errors from replies, use `errorHandler` in module options and pass class that extends `RMQErrorHandler`.
- **serviceName** (string) - service name for debugging.
- **autoBindingRoutes** (boolean) - set false you want to manage route binding manualy. Default to `true`.


```typescript
class LogMiddleware extends RMQPipeClass {
Expand Down Expand Up @@ -339,6 +342,43 @@ You can get all message properties that RMQ gets. Example:
}
```

## TSL/SSL support
To configure certificates and learn why do you need it, [read here](https://www.rabbitmq.com/ssl.html).

To use `amqps` connection:

``` typescript
RMQModule.forRoot({
exchangeName: 'test',
connections: [
{
protocol: RMQ_PROTOCOL.AMQPS, // new
login: 'admin',
password: 'admin',
host: 'localhost',
},
],
connectionOptions: {
cert: fs.readFileSync('clientcert.pem'),
key: fs.readFileSync('clientkey.pem'),
passphrase: 'MySecretPassword',
ca: [fs.readFileSync('cacert.pem')]
} // new
}),
```

This is the basic example with reading files, but you can do however you want. `cert`, `key` and `ca` must be Buffers. Notice: `ca` is array. If you don't need keys, just use `RMQ_PROTOCOL.AMQPS` protocol.

To use it with `pkcs12` files:

``` typescript
connectionOptions: {
pfx: fs.readFileSync('clientcertkey.p12'),
passphrase: 'MySecretPassword',
ca: [fs.readFileSync('cacert.pem')]
},
```

## Manual message Ack/Nack

If you want to use your own [ack](https://www.squaremobius.net/amqp.node/channel_api.html#channel_nack)/[nack](https://www.squaremobius.net/amqp.node/channel_api.html#channel_ack) logic, you can set manual acknowledgement to `@RMQRoute`. Than in any place you have to manually ack/nack message that you get with `@RMQMessage`.
Expand Down Expand Up @@ -442,19 +482,19 @@ customMessageFactory({ num }: CustomMessageFactoryContracts.Request, appId: stri

## Validating data

NestJS-rmq uses [class-validator](https://github.com/typestack/class-validator) to validate incoming data. To use it, decorate your route method with `Validate`:
NestJS-rmq uses [class-validator](https://github.com/typestack/class-validator) to validate incoming data. To use it, decorate your route method with `RMQValidate`:

```typescript
import { RMQRoute, Validate } from 'nestjs-rmq';
import { RMQRoute, RMQValidate } from 'nestjs-rmq';

@RMQValidate()
@RMQRoute('my.rpc')
@Validate()
myMethod(data: myClass): number {
// ...
}
```

Add it after `@RMQRoute()`. Where `myClass` is data class with validation decorators:
Where `myClass` is data class with validation decorators:

```typescript
import { IsString, MinLength, IsNumber } from 'class-validator';
Expand All @@ -471,6 +511,36 @@ export class myClass {

If your input data will be invalid, the library will send back an error without even entering your method. This will prevent you from manually validating your data inside route. You can check all available validators [here](https://github.com/typestack/class-validator).

## Transforming data

NestJS-rmq uses [class-transformer](https://github.com/typestack/class-transformer) to transform incoming data. To use it, decorate your route method with `RMQTransform`:

```typescript
import { RMQRoute, RMQTransform } from 'nestjs-rmq';

@RMQTransform()
@RMQValidate()
@RMQRoute('my.rpc')
myMethod(data: myClass): number {
// ...
}
```

Where `myClass` is data class with transformation decorators:

```typescript
import { Type } from 'class-transformer';
import { IsDate } from 'class-validator';

export class myClass {
@IsDate()
@Type(() => Date)
date: Date;
}
```

After this you can use `data.date` in your controller as Date object and not a string. You can check class-validator docs [here](https://github.com/typestack/class-transformer). You can use transformation and validation at the same time - first transformation will be applied and then validation.

## Using pipes

To intercept any message to any route, you can use `@RMQPipe` decorator:
Expand Down
13 changes: 13 additions & 0 deletions e2e/contracts/mock.contracts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Type } from 'class-transformer';
import { IsNumber, IsString } from 'class-validator';

export namespace SumContracts {
Expand Down Expand Up @@ -112,4 +113,16 @@ export namespace PatternHashContracts {
export class Response {
num: number;
}
}

export namespace TransformContracts {
export const topic: string = 'transform.rpc';
export class Request {
@Type(() => Date)
date: Date;
}
export class Response {
res: number;
type: string;
}
}
5 changes: 5 additions & 0 deletions e2e/mocks/api.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
PatternStarContracts,
SumContracts,
TimeOutContracts,
TransformContracts,
} from '../contracts/mock.contracts';

@Controller()
Expand Down Expand Up @@ -77,4 +78,8 @@ export class ApiController {
async hash(num: number): Promise<PatternHashContracts.Response> {
return this.rmq.send<PatternHashContracts.Request, PatternHashContracts.Response>('this.is.hash', { num });
}

async tarnsform(date: Date): Promise<TransformContracts.Response> {
return this.rmq.send<TransformContracts.Request, TransformContracts.Response>(TransformContracts.topic, { date });
}
}
18 changes: 12 additions & 6 deletions e2e/mocks/microservice.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Controller } from '@nestjs/common';
import { RMQMessage, RMQError, RMQRoute, Validate, ExtendedMessage, RMQService } from '../../lib';
import { RMQMessage, RMQError, RMQRoute, RMQValidate, ExtendedMessage, RMQService, RMQTransform } from '../../lib';
import {
DivideContracts,
MultiplyContracts,
Expand All @@ -8,7 +8,7 @@ import {
TimeOutContracts,
AppIdContracts,
ManualAckContracts,
DebugContracts, CustomMessageFactoryContracts, PatternStarContracts, PatternHashContracts,
DebugContracts, CustomMessageFactoryContracts, PatternStarContracts, PatternHashContracts, TransformContracts,
} from '../contracts/mock.contracts';
import { ERROR_TYPE } from '../../lib/constants';
import { Message } from 'amqplib';
Expand All @@ -18,7 +18,7 @@ export class MicroserviceController {
constructor(private readonly rmqService: RMQService) { }

@RMQRoute(SumContracts.topic)
@Validate()
@RMQValidate()
sumRpc({ arrayToSum }: SumContracts.Request): SumContracts.Response {
const result = arrayToSum.reduce((prev, cur) => prev + cur);
if (result === 0) {
Expand All @@ -34,20 +34,20 @@ export class MicroserviceController {
}

@RMQRoute(NotificationContracts.topic)
@Validate()
@RMQValidate()
notificationNone({ message }: NotificationContracts.Request): void {
console.log(message);
return;
}

@RMQRoute(MultiplyContracts.topic)
@Validate()
@RMQValidate()
multiplyRpc({ arrayToMultiply }: MultiplyContracts.Request): MultiplyContracts.Response {
return { result: arrayToMultiply.reduce((prev, cur) => prev * cur) };
}

@RMQRoute(DivideContracts.topic)
@Validate()
@RMQValidate()
divide({ first, second }: DivideContracts.Request): DivideContracts.Response {
return { result: first / second };
}
Expand Down Expand Up @@ -99,4 +99,10 @@ export class MicroserviceController {
hashPattern({ num }: PatternHashContracts.Request): PatternHashContracts.Response {
return { num };
}

@RMQTransform()
@RMQRoute(TransformContracts.topic)
transform({ date }: TransformContracts.Request): TransformContracts.Response {
return { res: date.getFullYear() + 1, type: typeof date };
}
}
5 changes: 5 additions & 0 deletions e2e/tests/rmq.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ describe('RMQe2e', () => {
const { debugString } = await apiController.debug();
expect(debugString).toContain('"message":{"prop1":[1],"prop2":"Buffer - length 11"}');
});
it('request transform', async () => {
const { res, type } = await apiController.tarnsform(new Date('01-01-2021'));
expect(res).toBe(2022);
expect(type).toBe('object');
});
it('request validation failed', async () => {
try {
await apiController.sumFailed(['a', 'b', 'c']);
Expand Down
6 changes: 1 addition & 5 deletions e2e/tests/rmqAsync.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { ConfigService } from '../mocks/config.service';
describe('RMQe2e', () => {
let api: INestApplication;
let apiController: ApiController;
let microserviceController: MicroserviceController;
let rmqService: RMQService;

beforeAll(async () => {
Expand All @@ -30,7 +29,7 @@ describe('RMQe2e', () => {
},
],
serviceName: 'test-service',
queueName: 'test',
queueName: 'test'
};
},
}),
Expand All @@ -41,10 +40,7 @@ describe('RMQe2e', () => {
await api.init();

apiController = apiModule.get<ApiController>(ApiController);
microserviceController = apiModule.get<MicroserviceController>(MicroserviceController);
rmqService = apiModule.get<RMQService>(RMQService);
console.warn = jest.fn();
console.log = jest.fn();
});

describe('rpc', () => {
Expand Down
Loading

0 comments on commit 384e386

Please sign in to comment.