@makebelieve21213-packages/rabbitmq-client 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +461 -0
- package/dist/__tests__/__mocks__/logger.d.ts +10 -0
- package/dist/__tests__/__mocks__/logger.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/logger.js +11 -0
- package/dist/__tests__/__mocks__/logger.js.map +1 -0
- package/dist/__tests__/__mocks__/redis-client.d.ts +9 -0
- package/dist/__tests__/__mocks__/redis-client.d.ts.map +1 -0
- package/dist/__tests__/__mocks__/redis-client.js +10 -0
- package/dist/__tests__/__mocks__/redis-client.js.map +1 -0
- package/dist/__tests__/connect-rabbitmq-receiver.spec.d.ts +2 -0
- package/dist/__tests__/connect-rabbitmq-receiver.spec.d.ts.map +1 -0
- package/dist/__tests__/connect-rabbitmq-receiver.spec.js +206 -0
- package/dist/__tests__/connect-rabbitmq-receiver.spec.js.map +1 -0
- package/dist/__tests__/connect-rabbitmq-receivers.spec.d.ts +2 -0
- package/dist/__tests__/connect-rabbitmq-receivers.spec.d.ts.map +1 -0
- package/dist/__tests__/connect-rabbitmq-receivers.spec.js +139 -0
- package/dist/__tests__/connect-rabbitmq-receivers.spec.js.map +1 -0
- package/dist/__tests__/index.spec.d.ts +2 -0
- package/dist/__tests__/index.spec.d.ts.map +1 -0
- package/dist/__tests__/index.spec.js +206 -0
- package/dist/__tests__/index.spec.js.map +1 -0
- package/dist/__tests__/setup.d.ts +2 -0
- package/dist/__tests__/setup.d.ts.map +1 -0
- package/dist/__tests__/setup.js +15 -0
- package/dist/__tests__/setup.js.map +1 -0
- package/dist/__tests__/test-types.d.ts +52 -0
- package/dist/__tests__/test-types.d.ts.map +1 -0
- package/dist/__tests__/test-types.js +2 -0
- package/dist/__tests__/test-types.js.map +1 -0
- package/dist/config/__tests__/factories.spec.d.ts +2 -0
- package/dist/config/__tests__/factories.spec.d.ts.map +1 -0
- package/dist/config/__tests__/factories.spec.js +168 -0
- package/dist/config/__tests__/factories.spec.js.map +1 -0
- package/dist/config/factories.d.ts +23 -0
- package/dist/config/factories.d.ts.map +1 -0
- package/dist/config/factories.js +123 -0
- package/dist/config/factories.js.map +1 -0
- package/dist/connect-rabbitmq-receiver.d.ts +8 -0
- package/dist/connect-rabbitmq-receiver.d.ts.map +1 -0
- package/dist/connect-rabbitmq-receiver.js +49 -0
- package/dist/connect-rabbitmq-receiver.js.map +1 -0
- package/dist/connect-rabbitmq-receivers.d.ts +8 -0
- package/dist/connect-rabbitmq-receivers.d.ts.map +1 -0
- package/dist/connect-rabbitmq-receivers.js +18 -0
- package/dist/connect-rabbitmq-receivers.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/__tests__/rabbitmq.interceptor.spec.d.ts +2 -0
- package/dist/interceptors/__tests__/rabbitmq.interceptor.spec.d.ts.map +1 -0
- package/dist/interceptors/__tests__/rabbitmq.interceptor.spec.js +647 -0
- package/dist/interceptors/__tests__/rabbitmq.interceptor.spec.js.map +1 -0
- package/dist/interceptors/rabbitmq.interceptor.d.ts +18 -0
- package/dist/interceptors/rabbitmq.interceptor.d.ts.map +1 -0
- package/dist/interceptors/rabbitmq.interceptor.js +107 -0
- package/dist/interceptors/rabbitmq.interceptor.js.map +1 -0
- package/dist/main/__tests__/rabbitmq.module.spec.d.ts +2 -0
- package/dist/main/__tests__/rabbitmq.module.spec.d.ts.map +1 -0
- package/dist/main/__tests__/rabbitmq.module.spec.js +266 -0
- package/dist/main/__tests__/rabbitmq.module.spec.js.map +1 -0
- package/dist/main/__tests__/rabbitmq.service.spec.d.ts +2 -0
- package/dist/main/__tests__/rabbitmq.service.spec.d.ts.map +1 -0
- package/dist/main/__tests__/rabbitmq.service.spec.js +349 -0
- package/dist/main/__tests__/rabbitmq.service.spec.js.map +1 -0
- package/dist/main/rabbitmq.module.d.ts +10 -0
- package/dist/main/rabbitmq.module.d.ts.map +1 -0
- package/dist/main/rabbitmq.module.js +51 -0
- package/dist/main/rabbitmq.module.js.map +1 -0
- package/dist/main/rabbitmq.service.d.ts +26 -0
- package/dist/main/rabbitmq.service.d.ts.map +1 -0
- package/dist/main/rabbitmq.service.js +100 -0
- package/dist/main/rabbitmq.service.js.map +1 -0
- package/dist/types/base.config.d.ts +15 -0
- package/dist/types/base.config.d.ts.map +1 -0
- package/dist/types/base.config.js +2 -0
- package/dist/types/base.config.js.map +1 -0
- package/dist/types/idempotent-message.d.ts +6 -0
- package/dist/types/idempotent-message.d.ts.map +1 -0
- package/dist/types/idempotent-message.js +2 -0
- package/dist/types/idempotent-message.js.map +1 -0
- package/dist/types/message-with-correlation-id.d.ts +10 -0
- package/dist/types/message-with-correlation-id.d.ts.map +1 -0
- package/dist/types/message-with-correlation-id.js +2 -0
- package/dist/types/message-with-correlation-id.js.map +1 -0
- package/dist/types/rabbitmq-config-dlx.d.ts +4 -0
- package/dist/types/rabbitmq-config-dlx.d.ts.map +1 -0
- package/dist/types/rabbitmq-config-dlx.js +2 -0
- package/dist/types/rabbitmq-config-dlx.js.map +1 -0
- package/dist/types/rabbitmq-config-receiver.d.ts +15 -0
- package/dist/types/rabbitmq-config-receiver.d.ts.map +1 -0
- package/dist/types/rabbitmq-config-receiver.js +2 -0
- package/dist/types/rabbitmq-config-receiver.js.map +1 -0
- package/dist/types/rabbitmq-config-retry.d.ts +13 -0
- package/dist/types/rabbitmq-config-retry.d.ts.map +1 -0
- package/dist/types/rabbitmq-config-retry.js +2 -0
- package/dist/types/rabbitmq-config-retry.js.map +1 -0
- package/dist/types/rabbitmq-config-sender.d.ts +14 -0
- package/dist/types/rabbitmq-config-sender.d.ts.map +1 -0
- package/dist/types/rabbitmq-config-sender.js +2 -0
- package/dist/types/rabbitmq-config-sender.js.map +1 -0
- package/dist/types/rabbitmq-module-options.d.ts +17 -0
- package/dist/types/rabbitmq-module-options.d.ts.map +1 -0
- package/dist/types/rabbitmq-module-options.js +2 -0
- package/dist/types/rabbitmq-module-options.js.map +1 -0
- package/dist/types/rabbitmq-receiver-options.d.ts +18 -0
- package/dist/types/rabbitmq-receiver-options.d.ts.map +1 -0
- package/dist/types/rabbitmq-receiver-options.js +2 -0
- package/dist/types/rabbitmq-receiver-options.js.map +1 -0
- package/dist/types/rabbitmq-receiver-subscription.d.ts +19 -0
- package/dist/types/rabbitmq-receiver-subscription.d.ts.map +1 -0
- package/dist/types/rabbitmq-receiver-subscription.js +2 -0
- package/dist/types/rabbitmq-receiver-subscription.js.map +1 -0
- package/dist/types/rabbitmq-receivers-config.d.ts +14 -0
- package/dist/types/rabbitmq-receivers-config.d.ts.map +1 -0
- package/dist/types/rabbitmq-receivers-config.js +2 -0
- package/dist/types/rabbitmq-receivers-config.js.map +1 -0
- package/dist/types/rabbitmq-sender-options.d.ts +10 -0
- package/dist/types/rabbitmq-sender-options.d.ts.map +1 -0
- package/dist/types/rabbitmq-sender-options.js +2 -0
- package/dist/types/rabbitmq-sender-options.js.map +1 -0
- package/dist/types/routing-keys.d.ts +24 -0
- package/dist/types/routing-keys.d.ts.map +1 -0
- package/dist/types/routing-keys.js +29 -0
- package/dist/types/routing-keys.js.map +1 -0
- package/dist/utils/__tests__/injections.spec.d.ts +2 -0
- package/dist/utils/__tests__/injections.spec.d.ts.map +1 -0
- package/dist/utils/__tests__/injections.spec.js +174 -0
- package/dist/utils/__tests__/injections.spec.js.map +1 -0
- package/dist/utils/injections.d.ts +2 -0
- package/dist/utils/injections.d.ts.map +1 -0
- package/dist/utils/injections.js +2 -0
- package/dist/utils/injections.js.map +1 -0
- package/package.json +91 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Skriabin Aleksei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
# @makebelieve21213-packages/rabbitmq-client
|
|
2
|
+
|
|
3
|
+
RabbitMQ клиент для NestJS с поддержкой TypeScript и полной типобезопасностью. Обеспечивает надежную отправку и получение сообщений между микросервисами с поддержкой retry-механизмов, Dead Letter Queue (DLQ) и идемпотентности сообщений.
|
|
4
|
+
|
|
5
|
+
## 📋 Содержание
|
|
6
|
+
|
|
7
|
+
- [Возможности](#-возможности)
|
|
8
|
+
- [Требования](#-требования)
|
|
9
|
+
- [Установка](#-установка)
|
|
10
|
+
- [Структура пакета](#-структура-пакета)
|
|
11
|
+
- [Быстрый старт](#-быстрый-старт)
|
|
12
|
+
- [API Reference](#-api-reference)
|
|
13
|
+
- [Примеры использования](#-примеры-использования)
|
|
14
|
+
- [Troubleshooting](#-troubleshooting)
|
|
15
|
+
- [Тестирование](#-тестирование)
|
|
16
|
+
|
|
17
|
+
## 🚀 Возможности
|
|
18
|
+
|
|
19
|
+
- ✅ **NestJS интеграция** - глобальный модуль с forRootAsync для простой интеграции
|
|
20
|
+
- ✅ **Type-safe API** - полная типобезопасность TypeScript с экспортируемыми типами
|
|
21
|
+
- ✅ **Отправка и получение сообщений** - поддержка fire-and-forget и request-response паттернов
|
|
22
|
+
- ✅ **Идемпотентность** - автоматическая проверка дубликатов сообщений через Redis
|
|
23
|
+
- ✅ **Retry механизм** - автоматические повторные попытки с настраиваемым TTL
|
|
24
|
+
- ✅ **Dead Letter Queue** - обработка критических ошибок через DLX
|
|
25
|
+
- ✅ **Множественные подписки** - поддержка подключения нескольких очередей одновременно
|
|
26
|
+
- ✅ **Обработка ошибок** - интеграция с UnifiedExceptionFilter для корректной обработки ошибок
|
|
27
|
+
- ✅ **100% покрытие тестами** - надежность и качество кода
|
|
28
|
+
|
|
29
|
+
## 📋 Требования
|
|
30
|
+
|
|
31
|
+
- **Node.js**: >= 22.13.0
|
|
32
|
+
- **NestJS**: >= 11.0.0
|
|
33
|
+
- **RabbitMQ**: сервер RabbitMQ
|
|
34
|
+
- **Redis**: сервер Redis (обязательно для полноценного использования идемпотентности)
|
|
35
|
+
|
|
36
|
+
## 📦 Установка
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @makebelieve21213-packages/rabbitmq-client
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Зависимости
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"@nestjs/common": "^11.0.0",
|
|
47
|
+
"@nestjs/config": "^4.0.0",
|
|
48
|
+
"@nestjs/microservices": "^11.0.0",
|
|
49
|
+
"@makebelieve21213-packages/logger": "^1.0.0",
|
|
50
|
+
"@makebelieve21213-packages/nest-common": "^1.0.0",
|
|
51
|
+
"@makebelieve21213-packages/redis-client": "^1.0.0",
|
|
52
|
+
"rxjs": "^7.8.0",
|
|
53
|
+
"uuid": "^11.0.0"
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 📁 Структура пакета
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
src/
|
|
61
|
+
├── main/ # NestJS модуль и сервисы
|
|
62
|
+
├── config/ # Фабрики конфигураций
|
|
63
|
+
├── types/ # TypeScript типы
|
|
64
|
+
├── utils/ # Утилиты
|
|
65
|
+
├── connect-rabbitmq-receiver.ts # Функция подключения одной подписки
|
|
66
|
+
├── connect-rabbitmq-receivers.ts # Функция подключения множественных подписок
|
|
67
|
+
└── index.ts # Экспорты
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 🏗️ Архитектура
|
|
71
|
+
|
|
72
|
+
Пакет предоставляет NestJS глобальный модуль `RabbitMQModule` для отправки сообщений и функции `connectRabbitMQReceiver` / `connectRabbitMQReceivers` для получения сообщений через RabbitMQ.
|
|
73
|
+
|
|
74
|
+
**Основные компоненты:**
|
|
75
|
+
- `RabbitMQModule` - NestJS глобальный модуль для отправки
|
|
76
|
+
- `RabbitMQService` - сервис для отправки сообщений
|
|
77
|
+
- `connectRabbitMQReceiver` - функция подключения одной подписки
|
|
78
|
+
- `connectRabbitMQReceivers` - функция подключения множественных подписок
|
|
79
|
+
- `RabbitMQIdempotencyInterceptor` - интерцептор для проверки идемпотентности
|
|
80
|
+
- Типы: `RabbitMQSenderOptions`, `RabbitMQReceiverOptions`, `IdempotentMessage`
|
|
81
|
+
|
|
82
|
+
## 🔧 Быстрый старт
|
|
83
|
+
|
|
84
|
+
### Шаг 1: Настройка переменных окружения
|
|
85
|
+
|
|
86
|
+
```env
|
|
87
|
+
RABBITMQ_URL=amqp://localhost:5672
|
|
88
|
+
RABBITMQ_EXCHANGE=events_exchange
|
|
89
|
+
RABBITMQ_EXCHANGE_TYPE=topic
|
|
90
|
+
RABBITMQ_QUEUE=your-service-queue
|
|
91
|
+
RABBITMQ_PATTERN=your.pattern.*
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Шаг 2: Подключение Redis (обязательно для идемпотентности)
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { Module } from "@nestjs/common";
|
|
98
|
+
import { RedisModule } from "@makebelieve21213-packages/redis-client";
|
|
99
|
+
|
|
100
|
+
@Module({
|
|
101
|
+
imports: [
|
|
102
|
+
RedisModule.forRootAsync({
|
|
103
|
+
// конфигурация Redis
|
|
104
|
+
}),
|
|
105
|
+
],
|
|
106
|
+
})
|
|
107
|
+
export class AppModule {}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Важно:** Без подключенного `RedisService` в DI контейнере приложения интерцептор `RabbitMQIdempotencyInterceptor` не сможет проверять дубликаты сообщений и будет пропускать проверку идемпотентности (graceful degradation).
|
|
111
|
+
|
|
112
|
+
### Шаг 3: Создание конфигурации
|
|
113
|
+
|
|
114
|
+
Создайте файл `rabbitmq.config.ts` в вашем сервисе:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { registerAs } from "@nestjs/config";
|
|
118
|
+
import type { RabbitMQSenderOptions, RabbitMQReceiverOptions } from "@makebelieve21213-packages/rabbitmq-client";
|
|
119
|
+
import { EnvVariable } from "src/types/enums";
|
|
120
|
+
|
|
121
|
+
export type RabbitMQConfiguration = {
|
|
122
|
+
sender: RabbitMQSenderOptions;
|
|
123
|
+
receiver: RabbitMQReceiverOptions;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const rabbitmqConfig = registerAs<RabbitMQConfiguration>(
|
|
127
|
+
"rabbitmq",
|
|
128
|
+
(): RabbitMQConfiguration => ({
|
|
129
|
+
sender: {
|
|
130
|
+
url: process.env[EnvVariable.RABBITMQ_URL]!,
|
|
131
|
+
exchange: process.env[EnvVariable.RABBITMQ_EXCHANGE] || "events_exchange",
|
|
132
|
+
exchangeType: process.env[EnvVariable.RABBITMQ_EXCHANGE_TYPE] || "topic",
|
|
133
|
+
},
|
|
134
|
+
receiver: {
|
|
135
|
+
url: process.env[EnvVariable.RABBITMQ_URL]!,
|
|
136
|
+
exchange: process.env[EnvVariable.RABBITMQ_EXCHANGE] || "events_exchange",
|
|
137
|
+
exchangeType: process.env[EnvVariable.RABBITMQ_EXCHANGE_TYPE] || "topic",
|
|
138
|
+
queue: process.env[EnvVariable.RABBITMQ_QUEUE]!,
|
|
139
|
+
pattern: process.env[EnvVariable.RABBITMQ_PATTERN]!,
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
export default rabbitmqConfig;
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Шаг 4: Регистрация модуля
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// app.module.ts
|
|
151
|
+
import { Module } from '@nestjs/common';
|
|
152
|
+
import { ConfigModule } from '@nestjs/config';
|
|
153
|
+
import { RabbitMQModule } from '@makebelieve21213-packages/rabbitmq-client';
|
|
154
|
+
import rabbitmqConfig from 'src/configs/rabbitmq.config';
|
|
155
|
+
import type { RabbitMQConfiguration } from 'src/configs/rabbitmq.config';
|
|
156
|
+
|
|
157
|
+
@Module({
|
|
158
|
+
imports: [
|
|
159
|
+
ConfigModule.forRoot({
|
|
160
|
+
isGlobal: true,
|
|
161
|
+
load: [rabbitmqConfig],
|
|
162
|
+
}),
|
|
163
|
+
RabbitMQModule.forRootAsync<[RabbitMQConfiguration]>({
|
|
164
|
+
useFactory: (config: RabbitMQConfiguration) => config.sender,
|
|
165
|
+
inject: [rabbitmqConfig.KEY],
|
|
166
|
+
imports: [ConfigModule],
|
|
167
|
+
}),
|
|
168
|
+
],
|
|
169
|
+
})
|
|
170
|
+
export class AppModule {}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Шаг 5: Подключение receiver в main.ts
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// main.ts
|
|
177
|
+
import { ConfigService } from "@nestjs/config";
|
|
178
|
+
import { NestFactory } from "@nestjs/core";
|
|
179
|
+
import { connectLogger } from "@makebelieve21213-packages/logger";
|
|
180
|
+
import { UnifiedExceptionFilter, UnifiedInterceptor } from "@makebelieve21213-packages/nest-common";
|
|
181
|
+
import { connectRabbitMQReceiver, type RabbitMQReceiverOptions } from "@makebelieve21213-packages/rabbitmq-client";
|
|
182
|
+
import { AppModule } from "./app.module";
|
|
183
|
+
|
|
184
|
+
async function bootstrap() {
|
|
185
|
+
const app = await NestFactory.create(AppModule);
|
|
186
|
+
|
|
187
|
+
const config = app.get(ConfigService);
|
|
188
|
+
const receiverOptions = config.get<RabbitMQReceiverOptions>("rabbitmq.receiver")!;
|
|
189
|
+
|
|
190
|
+
// Перезаписываем стандартный логгер сервиса
|
|
191
|
+
const logger = await connectLogger(app, "YourService");
|
|
192
|
+
|
|
193
|
+
// Получаем dlxExchange из конфигурации RabbitMQ для RPC обработки ошибок
|
|
194
|
+
const dlxExchange = receiverOptions.dlxExchange || "events_exchange.dlx";
|
|
195
|
+
|
|
196
|
+
// Подключаем глобально единый фильтр для HTTP и RPC запросов (обработка ошибок)
|
|
197
|
+
app.useGlobalFilters(new UnifiedExceptionFilter(logger, dlxExchange));
|
|
198
|
+
|
|
199
|
+
// Подключаем глобально единый перехватчик для HTTP и RPC запросов (логирование и метрики всех запросов)
|
|
200
|
+
app.useGlobalInterceptors(new UnifiedInterceptor(logger));
|
|
201
|
+
|
|
202
|
+
// Подключаем RabbitMQ микросервис для получения сообщений
|
|
203
|
+
await connectRabbitMQReceiver(app, receiverOptions);
|
|
204
|
+
|
|
205
|
+
await app.startAllMicroservices();
|
|
206
|
+
await app.listen(3000);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
bootstrap();
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Шаг 6: Использование сервиса
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// your.service.ts
|
|
216
|
+
import { Injectable } from '@nestjs/common';
|
|
217
|
+
import { RabbitMQService } from '@makebelieve21213-packages/rabbitmq-client';
|
|
218
|
+
import { ROUTING_KEYS } from '@makebelieve21213-packages/rabbitmq-client';
|
|
219
|
+
|
|
220
|
+
@Injectable()
|
|
221
|
+
export class YourService {
|
|
222
|
+
constructor(private readonly rabbitMQ: RabbitMQService) {}
|
|
223
|
+
|
|
224
|
+
async sendFireAndForget() {
|
|
225
|
+
await this.rabbitMQ.fireAndForget(ROUTING_KEYS.ANALYTICS_UPDATE_GLOBAL, {
|
|
226
|
+
data: "example"
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async sendRequestResponse() {
|
|
231
|
+
const result = await this.rabbitMQ.publish<RequestData, ResponseData>(
|
|
232
|
+
ROUTING_KEYS.ANALYTICS_GLOBAL,
|
|
233
|
+
{ query: "analytics" }
|
|
234
|
+
);
|
|
235
|
+
return result;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## 📚 API Reference
|
|
241
|
+
|
|
242
|
+
### RabbitMQModule
|
|
243
|
+
|
|
244
|
+
**forRootAsync(options):**
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
RabbitMQModule.forRootAsync<[RabbitMQConfiguration]>({
|
|
248
|
+
useFactory: (config: RabbitMQConfiguration) => config.sender,
|
|
249
|
+
inject: [rabbitmqConfig.KEY],
|
|
250
|
+
imports: [ConfigModule],
|
|
251
|
+
})
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Экспортирует:** `RabbitMQService` (глобально)
|
|
255
|
+
|
|
256
|
+
### RabbitMQService
|
|
257
|
+
|
|
258
|
+
**Методы:**
|
|
259
|
+
|
|
260
|
+
#### `fireAndForget<I>(key: ROUTING_KEYS, data: I)`
|
|
261
|
+
|
|
262
|
+
Отправляет сообщение без ожидания ответа (fire-and-forget паттерн). Не добавляет `correlationId`.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
fireAndForget<I>(key: ROUTING_KEYS, data: I): void
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### `publish<I, O>(key: ROUTING_KEYS, data: I): Promise<O>`
|
|
269
|
+
|
|
270
|
+
Отправляет сообщение и ожидает ответ (RPC паттерн). Автоматически добавляет `correlationId` и `correlationTimestamp` для идемпотентности.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
publish<I, O>(key: ROUTING_KEYS, data: I): Promise<O>
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### connectRabbitMQReceiver
|
|
277
|
+
|
|
278
|
+
Асинхронная функция для подключения одной подписки RabbitMQ.
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
connectRabbitMQReceiver(
|
|
282
|
+
app: INestApplication,
|
|
283
|
+
receiverOptions: RabbitMQReceiverOptions,
|
|
284
|
+
skipGlobalSetup?: boolean
|
|
285
|
+
): Promise<void>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Параметры:**
|
|
289
|
+
- `app` - экземпляр NestJS приложения
|
|
290
|
+
- `receiverOptions` - опции для настройки receiver
|
|
291
|
+
- `skipGlobalSetup` (опционально) - пропустить установку глобальных компонентов
|
|
292
|
+
|
|
293
|
+
### connectRabbitMQReceivers
|
|
294
|
+
|
|
295
|
+
Асинхронная функция для подключения множественных подписок RabbitMQ.
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
connectRabbitMQReceivers(
|
|
299
|
+
app: INestApplication,
|
|
300
|
+
receiverOptionsList: RabbitMQReceiverOptions[]
|
|
301
|
+
): Promise<void>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Параметры:**
|
|
305
|
+
- `app` - экземпляр NestJS приложения
|
|
306
|
+
- `receiverOptionsList` - массив опций для настройки receivers
|
|
307
|
+
|
|
308
|
+
### RabbitMQIdempotencyInterceptor
|
|
309
|
+
|
|
310
|
+
Глобальный интерцептор для проверки идемпотентности сообщений. Устанавливается автоматически в `connectRabbitMQReceiver` (если `skipGlobalSetup = false`).
|
|
311
|
+
|
|
312
|
+
**⚠️ Важно:** Требует наличия `RedisService` из пакета `@makebelieve21213-packages/redis-client` в DI контейнере приложения.
|
|
313
|
+
|
|
314
|
+
## 🧪 Примеры использования
|
|
315
|
+
|
|
316
|
+
### Отправка fire-and-forget сообщения
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
await this.rabbitMQ.fireAndForget(ROUTING_KEYS.ANALYTICS_UPDATE_GLOBAL, {
|
|
320
|
+
data: "example"
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Отправка request-response сообщения
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const result = await this.rabbitMQ.publish<RequestData, ResponseData>(
|
|
328
|
+
ROUTING_KEYS.ANALYTICS_GLOBAL,
|
|
329
|
+
{ query: "analytics" }
|
|
330
|
+
);
|
|
331
|
+
console.log(result);
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Создание обработчика сообщений
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { Controller } from "@nestjs/common";
|
|
338
|
+
import { MessagePattern, RmqContext, Ctx } from "@nestjs/microservices";
|
|
339
|
+
import { ROUTING_KEYS } from "@makebelieve21213-packages/types";
|
|
340
|
+
import { RpcError, RpcErrorType } from "@makebelieve21213-packages/nest-common";
|
|
341
|
+
|
|
342
|
+
@Controller()
|
|
343
|
+
export class MessageController {
|
|
344
|
+
@MessagePattern(ROUTING_KEYS.TOKENS_FETCH_ALL)
|
|
345
|
+
async handleTokenFetch(data: unknown, @Ctx() context: RmqContext) {
|
|
346
|
+
try {
|
|
347
|
+
// Ваша логика обработки
|
|
348
|
+
return { success: true };
|
|
349
|
+
} catch (error) {
|
|
350
|
+
throw new RpcError(
|
|
351
|
+
"Failed to fetch tokens",
|
|
352
|
+
RpcErrorType.INTERNAL_ERROR,
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Подключение множественных подписок
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { connectRabbitMQReceivers } from "@makebelieve21213-packages/rabbitmq-client";
|
|
363
|
+
|
|
364
|
+
async function bootstrap() {
|
|
365
|
+
const app = await NestFactory.create(AppModule);
|
|
366
|
+
|
|
367
|
+
const config = app.get(ConfigService);
|
|
368
|
+
const receiverOptionsList = config.get<RabbitMQReceiverOptions[]>("rabbitmq.receivers")!;
|
|
369
|
+
|
|
370
|
+
const logger = await connectLogger(app, "YourService");
|
|
371
|
+
const dlxExchange = receiverOptionsList[0]?.dlxExchange || "events_exchange.dlx";
|
|
372
|
+
|
|
373
|
+
app.useGlobalFilters(new UnifiedExceptionFilter(logger, dlxExchange));
|
|
374
|
+
app.useGlobalInterceptors(new UnifiedInterceptor(logger));
|
|
375
|
+
|
|
376
|
+
await connectRabbitMQReceivers(app, receiverOptionsList);
|
|
377
|
+
|
|
378
|
+
await app.startAllMicroservices();
|
|
379
|
+
await app.listen(3000);
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## 🚨 Troubleshooting
|
|
384
|
+
|
|
385
|
+
### Идемпотентность не работает
|
|
386
|
+
|
|
387
|
+
**Решение:** Убедитесь, что `RedisModule` подключен в корневом модуле приложения. Без Redis интерцептор будет пропускать проверку идемпотентности (graceful degradation).
|
|
388
|
+
|
|
389
|
+
### Сообщения не доставляются
|
|
390
|
+
|
|
391
|
+
**Решение:** Проверьте конфигурацию RabbitMQ (URL, exchange, queue, pattern), убедитесь, что сервер RabbitMQ запущен и доступен.
|
|
392
|
+
|
|
393
|
+
### Ошибки не обрабатываются корректно
|
|
394
|
+
|
|
395
|
+
**Решение:** Убедитесь, что `UnifiedExceptionFilter` и `UnifiedInterceptor` установлены в `main.ts` перед вызовом `connectRabbitMQReceiver` или `connectRabbitMQReceivers`.
|
|
396
|
+
|
|
397
|
+
### Сообщения застревают в очереди
|
|
398
|
+
|
|
399
|
+
**Решение:** Проверьте обработчики сообщений - они должны корректно обрабатывать сообщения и возвращать результат или выбрасывать `RpcError` для ошибок.
|
|
400
|
+
|
|
401
|
+
## 🧪 Тестирование
|
|
402
|
+
|
|
403
|
+
Пакет имеет **высокое покрытие тестами** (>95% для веток, 100% для statements и функций).
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
pnpm test # Все тесты
|
|
407
|
+
pnpm test:coverage # С покрытием
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## 🔧 Конфигурация
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
interface RabbitMQSenderOptions {
|
|
414
|
+
url: string; // URL подключения к RabbitMQ
|
|
415
|
+
exchange: string; // Имя основного exchange
|
|
416
|
+
exchangeType: string; // Тип exchange (обычно "topic")
|
|
417
|
+
replyQueueOptions?: { // Опции для reply очередей RPC паттерна
|
|
418
|
+
durable?: boolean; // По умолчанию: false
|
|
419
|
+
autoDelete?: boolean; // По умолчанию: true
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
interface RabbitMQReceiverOptions {
|
|
424
|
+
url: string; // URL подключения к RabbitMQ
|
|
425
|
+
exchange: string; // Имя основного exchange
|
|
426
|
+
exchangeType: string; // Тип exchange
|
|
427
|
+
queue: string; // Имя основной очереди
|
|
428
|
+
pattern: string; // Паттерн routing key
|
|
429
|
+
prefetchCount?: number; // По умолчанию: 10
|
|
430
|
+
noAck?: boolean; // По умолчанию: false
|
|
431
|
+
replyQueue?: string; // По умолчанию: ${queue}.reply
|
|
432
|
+
retryQueue?: string; // По умолчанию: ${queue}.retry
|
|
433
|
+
retryExchange?: string; // По умолчанию: ${exchange}.retry
|
|
434
|
+
retryExchangeType?: string; // По умолчанию: exchangeType
|
|
435
|
+
retryTtl?: number; // По умолчанию: 5000
|
|
436
|
+
dlxQueue?: string; // По умолчанию: global.dlx
|
|
437
|
+
dlxExchange?: string; // По умолчанию: events_exchange.dlx
|
|
438
|
+
dlxExchangeType?: string; // По умолчанию: exchangeType
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Примечание:** Конфигурация должна создаваться в сервисе, который использует пакет.
|
|
443
|
+
|
|
444
|
+
## 📦 Зависимости
|
|
445
|
+
|
|
446
|
+
- `@nestjs/common` - NestJS core
|
|
447
|
+
- `@nestjs/config` - NestJS config
|
|
448
|
+
- `@nestjs/microservices` - NestJS microservices
|
|
449
|
+
- `@makebelieve21213-packages/logger` - Логирование
|
|
450
|
+
- `@makebelieve21213-packages/nest-common` - Общие компоненты NestJS (RpcError, UnifiedExceptionFilter, UnifiedInterceptor)
|
|
451
|
+
- `@makebelieve21213-packages/redis-client` - Redis клиент для идемпотентности
|
|
452
|
+
- `rxjs` - Реактивные расширения
|
|
453
|
+
- `uuid` - Генерация уникальных ID
|
|
454
|
+
|
|
455
|
+
## 📄 Лицензия
|
|
456
|
+
|
|
457
|
+
MIT
|
|
458
|
+
|
|
459
|
+
## 👥 Автор
|
|
460
|
+
|
|
461
|
+
Skriabin Aleksei
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class LoggerService {
|
|
2
|
+
log: jest.Mock<any, any, any>;
|
|
3
|
+
error: jest.Mock<any, any, any>;
|
|
4
|
+
warn: jest.Mock<any, any, any>;
|
|
5
|
+
debug: jest.Mock<any, any, any>;
|
|
6
|
+
info: jest.Mock<any, any, any>;
|
|
7
|
+
setContext: jest.Mock<any, any, any>;
|
|
8
|
+
}
|
|
9
|
+
export default LoggerService;
|
|
10
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/__tests__/__mocks__/logger.ts"],"names":[],"mappings":"AACA,qBAAa,aAAa;IACzB,GAAG,2BAAa;IAChB,KAAK,2BAAa;IAClB,IAAI,2BAAa;IACjB,KAAK,2BAAa;IAClB,IAAI,2BAAa;IACjB,UAAU,2BAAa;CACvB;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Мок для @makebelieve21213-packages/logger
|
|
2
|
+
export class LoggerService {
|
|
3
|
+
log = jest.fn();
|
|
4
|
+
error = jest.fn();
|
|
5
|
+
warn = jest.fn();
|
|
6
|
+
debug = jest.fn();
|
|
7
|
+
info = jest.fn();
|
|
8
|
+
setContext = jest.fn();
|
|
9
|
+
}
|
|
10
|
+
export default LoggerService;
|
|
11
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/__tests__/__mocks__/logger.ts"],"names":[],"mappings":"AAAA,4CAA4C;AAC5C,MAAM,OAAO,aAAa;IACzB,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAChB,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAClB,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IACjB,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAClB,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IACjB,UAAU,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;CACvB;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class RedisClientService {
|
|
2
|
+
get: jest.Mock<any, any, any>;
|
|
3
|
+
set: jest.Mock<any, any, any>;
|
|
4
|
+
del: jest.Mock<any, any, any>;
|
|
5
|
+
exists: jest.Mock<any, any, any>;
|
|
6
|
+
hexists: jest.Mock<any, any, any>;
|
|
7
|
+
}
|
|
8
|
+
export default RedisClientService;
|
|
9
|
+
//# sourceMappingURL=redis-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-client.d.ts","sourceRoot":"","sources":["../../../src/__tests__/__mocks__/redis-client.ts"],"names":[],"mappings":"AACA,qBAAa,kBAAkB;IAC9B,GAAG,2BAAa;IAChB,GAAG,2BAAa;IAChB,GAAG,2BAAa;IAChB,MAAM,2BAAa;IACnB,OAAO,2BAAa;CACpB;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Мок для @makebelieve21213-packages/redis-client
|
|
2
|
+
export class RedisClientService {
|
|
3
|
+
get = jest.fn();
|
|
4
|
+
set = jest.fn();
|
|
5
|
+
del = jest.fn();
|
|
6
|
+
exists = jest.fn();
|
|
7
|
+
hexists = jest.fn();
|
|
8
|
+
}
|
|
9
|
+
export default RedisClientService;
|
|
10
|
+
//# sourceMappingURL=redis-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-client.js","sourceRoot":"","sources":["../../../src/__tests__/__mocks__/redis-client.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,OAAO,kBAAkB;IAC9B,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAChB,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAChB,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAChB,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IACnB,OAAO,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;CACpB;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-rabbitmq-receiver.spec.d.ts","sourceRoot":"","sources":["../../src/__tests__/connect-rabbitmq-receiver.spec.ts"],"names":[],"mappings":""}
|