@onlineapps/conn-infra-mq 1.1.3 → 1.1.4
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 +0 -0
- package/README.md +7 -7
- package/package.json +4 -4
- package/src/BaseClient.js +0 -0
- package/src/ConnectorMQClient.js +0 -0
- package/src/config/configSchema.js +0 -0
- package/src/config/defaultConfig.js +0 -0
- package/src/config/queueConfig.js +269 -0
- package/src/index.js +0 -0
- package/src/layers/ForkJoinHandler.js +0 -0
- package/src/layers/QueueManager.js +74 -5
- package/src/layers/RPCHandler.js +0 -0
- package/src/layers/RetryHandler.js +0 -0
- package/src/layers/WorkflowRouter.js +0 -0
- package/src/transports/rabbitmqClient.js +72 -3
- package/src/transports/transportFactory.js +0 -0
- package/src/utils/errorHandler.js +0 -0
- package/src/utils/logger.js +0 -0
- package/src/utils/serializer.js +0 -0
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# @onlineapps/
|
|
1
|
+
# @onlineapps/conn-infra-mq
|
|
2
2
|
|
|
3
|
-
[](https://github.com/onlineapps/conn-infra-mq/actions)
|
|
4
|
+
[](https://codecov.io/gh/onlineapps/conn-infra-mq)
|
|
5
|
+
[](https://www.npmjs.com/package/@onlineapps/conn-infra-mq)
|
|
6
6
|
|
|
7
7
|
> Message queue connector with **layered architecture** for workflow orchestration, RPC, fork-join, and retry patterns. Built on top of RabbitMQ with clean separation of concerns.
|
|
8
8
|
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
## 📦 Installation
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npm install @onlineapps/
|
|
29
|
+
npm install @onlineapps/conn-infra-mq
|
|
30
30
|
# or
|
|
31
|
-
yarn add @onlineapps/
|
|
31
|
+
yarn add @onlineapps/conn-infra-mq
|
|
32
32
|
````
|
|
33
33
|
|
|
34
34
|
> Requires Node.js ≥12. For RabbitMQ usage, ensure an accessible AMQP server.
|
|
@@ -52,7 +52,7 @@ ConnectorMQClient (main orchestrator)
|
|
|
52
52
|
```js
|
|
53
53
|
'use strict';
|
|
54
54
|
|
|
55
|
-
const ConnectorMQClient = require('@onlineapps/
|
|
55
|
+
const ConnectorMQClient = require('@onlineapps/conn-infra-mq');
|
|
56
56
|
|
|
57
57
|
(async () => {
|
|
58
58
|
// 1. Create client with configuration
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/conn-infra-mq",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "A promise-based, broker-agnostic client for sending and receiving messages via RabbitMQ",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
"test:integration:required": "REQUIRE_SERVICES=true jest --config=jest.integration.config.js",
|
|
22
22
|
"test:watch": "jest --watch",
|
|
23
23
|
"test:coverage": "jest --coverage --coverageReporters=text-lcov html",
|
|
24
|
-
"lint": "eslint \"src/**/*.js\" \"
|
|
25
|
-
"prettier:check": "prettier --check \"src/**/*.js\" \"
|
|
26
|
-
"prettier:fix": "prettier --write \"src/**/*.js\" \"
|
|
24
|
+
"lint": "eslint \"src/**/*.js\" \"tests/**/*.js\"",
|
|
25
|
+
"prettier:check": "prettier --check \"src/**/*.js\" \"tests/**/*.js\"",
|
|
26
|
+
"prettier:fix": "prettier --write \"src/**/*.js\" \"tests/**/*.js\""
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"connector",
|
package/src/BaseClient.js
CHANGED
|
File without changes
|
package/src/ConnectorMQClient.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* queueConfig.js
|
|
5
|
+
*
|
|
6
|
+
* Central configuration for INFRASTRUCTURE queues only.
|
|
7
|
+
* Business queues ({service}.workflow, {service}.queue, {service}.dlq) are created
|
|
8
|
+
* by individual services via QueueManager.setupServiceQueues().
|
|
9
|
+
*
|
|
10
|
+
* This ensures consistent configuration for infrastructure queues across all services.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
/**
|
|
15
|
+
* Workflow infrastructure queue configurations
|
|
16
|
+
* These are shared infrastructure queues used by all services
|
|
17
|
+
*/
|
|
18
|
+
workflow: {
|
|
19
|
+
/**
|
|
20
|
+
* workflow.init - Entry point for all workflows
|
|
21
|
+
* All services listen as competing consumers
|
|
22
|
+
*/
|
|
23
|
+
init: {
|
|
24
|
+
durable: true,
|
|
25
|
+
arguments: {
|
|
26
|
+
'x-message-ttl': 300000, // 5 minutes TTL
|
|
27
|
+
'x-max-length': 10000 // Max 10k messages
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* workflow.completed - Completed workflows
|
|
33
|
+
*/
|
|
34
|
+
completed: {
|
|
35
|
+
durable: true,
|
|
36
|
+
arguments: {
|
|
37
|
+
'x-message-ttl': 300000, // 5 minutes TTL
|
|
38
|
+
'x-max-length': 10000
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* workflow.failed - Failed workflows
|
|
44
|
+
*/
|
|
45
|
+
failed: {
|
|
46
|
+
durable: true,
|
|
47
|
+
arguments: {
|
|
48
|
+
'x-message-ttl': 300000, // 5 minutes TTL
|
|
49
|
+
'x-max-length': 10000
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* workflow.dlq - Dead letter queue for workflows
|
|
55
|
+
*/
|
|
56
|
+
dlq: {
|
|
57
|
+
durable: true,
|
|
58
|
+
arguments: {
|
|
59
|
+
// No TTL for DLQ - messages should persist
|
|
60
|
+
'x-max-length': 50000 // Higher limit for DLQ
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Business queue templates
|
|
67
|
+
* These are templates for service-specific queues ({service}.workflow, {service}.queue, {service}.dlq)
|
|
68
|
+
* Used by QueueManager.setupServiceQueues() to ensure consistent configuration
|
|
69
|
+
*/
|
|
70
|
+
business: {
|
|
71
|
+
/**
|
|
72
|
+
* {service}.workflow - Service-specific workflow queue
|
|
73
|
+
* Used for routing workflow steps to specific services
|
|
74
|
+
*/
|
|
75
|
+
workflow: {
|
|
76
|
+
durable: true,
|
|
77
|
+
arguments: {
|
|
78
|
+
'x-message-ttl': 300000, // 5 minutes TTL
|
|
79
|
+
'x-max-length': 10000, // Max 10k messages
|
|
80
|
+
'x-dead-letter-exchange': 'dlx',
|
|
81
|
+
'x-dead-letter-routing-key': '{service}.dlq' // Placeholder, replaced with actual service name
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* {service}.queue - Service main processing queue
|
|
87
|
+
* Used for direct service-to-service communication
|
|
88
|
+
*/
|
|
89
|
+
queue: {
|
|
90
|
+
durable: true,
|
|
91
|
+
arguments: {
|
|
92
|
+
'x-message-ttl': 30000, // 30 seconds TTL
|
|
93
|
+
'x-max-length': 10000, // Max 10k messages
|
|
94
|
+
'x-dead-letter-exchange': 'dlx',
|
|
95
|
+
'x-dead-letter-routing-key': '{service}.dlq' // Placeholder, replaced with actual service name
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* {service}.dlq - Service dead letter queue
|
|
101
|
+
* Receives failed messages from service.queue and service.workflow
|
|
102
|
+
*/
|
|
103
|
+
dlq: {
|
|
104
|
+
durable: true,
|
|
105
|
+
arguments: {
|
|
106
|
+
// No TTL for DLQ - messages should persist
|
|
107
|
+
'x-max-length': 50000 // Higher limit for DLQ
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Registry infrastructure queue configurations
|
|
114
|
+
*/
|
|
115
|
+
registry: {
|
|
116
|
+
/**
|
|
117
|
+
* registry.register - Registration requests
|
|
118
|
+
*/
|
|
119
|
+
register: {
|
|
120
|
+
durable: true,
|
|
121
|
+
arguments: {
|
|
122
|
+
'x-message-ttl': 60000, // 1 minute TTL
|
|
123
|
+
'x-max-length': 1000
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* registry.heartbeats - Service heartbeats
|
|
129
|
+
*/
|
|
130
|
+
heartbeats: {
|
|
131
|
+
durable: true,
|
|
132
|
+
arguments: {
|
|
133
|
+
'x-message-ttl': 120000, // 2 minutes TTL
|
|
134
|
+
'x-max-length': 5000
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* registry.changes - Registry event fanout exchange
|
|
140
|
+
* (This is an exchange, not a queue, but included for reference)
|
|
141
|
+
*/
|
|
142
|
+
changes: {
|
|
143
|
+
type: 'exchange',
|
|
144
|
+
durable: true
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get infrastructure queue configuration by type and name
|
|
150
|
+
* @param {string} type - Queue type: 'workflow', 'registry'
|
|
151
|
+
* @param {string} name - Queue name: 'init', 'completed', 'register', etc.
|
|
152
|
+
* @returns {Object} Queue configuration
|
|
153
|
+
*/
|
|
154
|
+
getQueueConfig(type, name) {
|
|
155
|
+
const config = this[type]?.[name];
|
|
156
|
+
if (!config) {
|
|
157
|
+
throw new Error(`Infrastructure queue config not found: ${type}.${name}`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Deep clone to avoid modifying original
|
|
161
|
+
return JSON.parse(JSON.stringify(config));
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get workflow infrastructure queue configuration
|
|
166
|
+
* @param {string} queueName - Queue name (e.g., 'workflow.init', 'workflow.completed')
|
|
167
|
+
* @returns {Object} Queue configuration
|
|
168
|
+
*/
|
|
169
|
+
getWorkflowQueueConfig(queueName) {
|
|
170
|
+
const parts = queueName.split('.');
|
|
171
|
+
if (parts.length !== 2 || parts[0] !== 'workflow') {
|
|
172
|
+
throw new Error(`Invalid workflow queue name: ${queueName}. Expected format: workflow.{name}`);
|
|
173
|
+
}
|
|
174
|
+
return this.getQueueConfig('workflow', parts[1]);
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get registry infrastructure queue configuration
|
|
179
|
+
* @param {string} queueName - Queue name (e.g., 'registry.register', 'registry.heartbeats')
|
|
180
|
+
* @returns {Object} Queue configuration
|
|
181
|
+
*/
|
|
182
|
+
getRegistryQueueConfig(queueName) {
|
|
183
|
+
const parts = queueName.split('.');
|
|
184
|
+
if (parts.length !== 2 || parts[0] !== 'registry') {
|
|
185
|
+
throw new Error(`Invalid registry queue name: ${queueName}. Expected format: registry.{name}`);
|
|
186
|
+
}
|
|
187
|
+
return this.getQueueConfig('registry', parts[1]);
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Check if queue name is an infrastructure queue
|
|
192
|
+
* @param {string} queueName - Queue name to check
|
|
193
|
+
* @returns {boolean} True if infrastructure queue
|
|
194
|
+
*/
|
|
195
|
+
isInfrastructureQueue(queueName) {
|
|
196
|
+
return queueName.startsWith('workflow.') || queueName.startsWith('registry.');
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Check if queue name is a business queue
|
|
201
|
+
* @param {string} queueName - Queue name to check
|
|
202
|
+
* @returns {boolean} True if business queue
|
|
203
|
+
*/
|
|
204
|
+
isBusinessQueue(queueName) {
|
|
205
|
+
// Business queues follow pattern: {service}.{type}
|
|
206
|
+
// where type is: workflow, queue, dlq
|
|
207
|
+
const parts = queueName.split('.');
|
|
208
|
+
if (parts.length !== 2) return false;
|
|
209
|
+
return ['workflow', 'queue', 'dlq'].includes(parts[1]);
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Parse business queue name to extract service name and queue type
|
|
214
|
+
* @param {string} queueName - Business queue name (e.g., 'hello-service.workflow')
|
|
215
|
+
* @returns {Object} { serviceName, queueType } or null if not a business queue
|
|
216
|
+
*/
|
|
217
|
+
parseBusinessQueue(queueName) {
|
|
218
|
+
if (!this.isBusinessQueue(queueName)) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
const parts = queueName.split('.');
|
|
222
|
+
return {
|
|
223
|
+
serviceName: parts[0],
|
|
224
|
+
queueType: parts[1]
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get business queue configuration template
|
|
230
|
+
* @param {string} queueType - Queue type: 'workflow', 'queue', 'dlq'
|
|
231
|
+
* @param {string} serviceName - Service name (replaces {service} placeholder)
|
|
232
|
+
* @returns {Object} Queue configuration
|
|
233
|
+
*/
|
|
234
|
+
getBusinessQueueConfig(queueType, serviceName) {
|
|
235
|
+
const template = this.business?.[queueType];
|
|
236
|
+
if (!template) {
|
|
237
|
+
throw new Error(`Business queue template not found: ${queueType}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Deep clone to avoid modifying original
|
|
241
|
+
const config = JSON.parse(JSON.stringify(template));
|
|
242
|
+
|
|
243
|
+
// Replace {service} placeholder in routing keys
|
|
244
|
+
if (config.arguments && serviceName) {
|
|
245
|
+
const routingKey = config.arguments['x-dead-letter-routing-key'];
|
|
246
|
+
if (routingKey && typeof routingKey === 'string') {
|
|
247
|
+
config.arguments['x-dead-letter-routing-key'] = routingKey.replace('{service}', serviceName);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return config;
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get infrastructure queue configuration by queue name (auto-detect type)
|
|
256
|
+
* @param {string} queueName - Full queue name (e.g., 'workflow.init', 'registry.register')
|
|
257
|
+
* @returns {Object} Queue configuration
|
|
258
|
+
*/
|
|
259
|
+
getInfrastructureQueueConfig(queueName) {
|
|
260
|
+
if (queueName.startsWith('workflow.')) {
|
|
261
|
+
return this.getWorkflowQueueConfig(queueName);
|
|
262
|
+
} else if (queueName.startsWith('registry.')) {
|
|
263
|
+
return this.getRegistryQueueConfig(queueName);
|
|
264
|
+
} else {
|
|
265
|
+
throw new Error(`Queue ${queueName} is not an infrastructure queue. Infrastructure queues must start with 'workflow.' or 'registry.'`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
package/src/index.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const queueConfig = require('../config/queueConfig');
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* QueueManager - Manages queue creation, configuration, and lifecycle
|
|
5
7
|
* Handles TTL, DLQ, auto-delete, and other queue properties
|
|
8
|
+
*
|
|
9
|
+
* For infrastructure queues (workflow.*, registry.*), uses central configuration.
|
|
10
|
+
* For business queues ({service}.*), uses service-specific configuration.
|
|
6
11
|
*/
|
|
7
12
|
class QueueManager {
|
|
8
13
|
constructor(mqClient, config = {}) {
|
|
@@ -19,8 +24,11 @@ class QueueManager {
|
|
|
19
24
|
|
|
20
25
|
/**
|
|
21
26
|
* Create or ensure queue exists with specific configuration
|
|
27
|
+
* For infrastructure queues (workflow.*, registry.*), uses central configuration.
|
|
28
|
+
* For business queues, uses provided options.
|
|
29
|
+
*
|
|
22
30
|
* @param {string} queueName - Name of the queue
|
|
23
|
-
* @param {Object} options - Queue configuration options
|
|
31
|
+
* @param {Object} options - Queue configuration options (ignored for infrastructure queues)
|
|
24
32
|
*/
|
|
25
33
|
async ensureQueue(queueName, options = {}) {
|
|
26
34
|
// Get channel from client's transport
|
|
@@ -30,6 +38,57 @@ class QueueManager {
|
|
|
30
38
|
}
|
|
31
39
|
const channel = transport.channel;
|
|
32
40
|
|
|
41
|
+
let queueOptions;
|
|
42
|
+
|
|
43
|
+
// Check if this is an infrastructure queue - use central config
|
|
44
|
+
if (queueConfig.isInfrastructureQueue(queueName)) {
|
|
45
|
+
try {
|
|
46
|
+
const infraConfig = queueConfig.getInfrastructureQueueConfig(queueName);
|
|
47
|
+
queueOptions = {
|
|
48
|
+
durable: infraConfig.durable !== false,
|
|
49
|
+
arguments: { ...infraConfig.arguments }
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
// If config not found, fall back to provided options
|
|
53
|
+
console.warn(`[QueueManager] Infrastructure queue config not found for ${queueName}, using provided options:`, error.message);
|
|
54
|
+
queueOptions = this._buildQueueOptions(queueName, options);
|
|
55
|
+
}
|
|
56
|
+
} else if (queueConfig.isBusinessQueue(queueName)) {
|
|
57
|
+
// Business queue - use central business queue config
|
|
58
|
+
try {
|
|
59
|
+
const parsed = queueConfig.parseBusinessQueue(queueName);
|
|
60
|
+
if (parsed) {
|
|
61
|
+
const businessConfig = queueConfig.getBusinessQueueConfig(parsed.queueType, parsed.serviceName);
|
|
62
|
+
queueOptions = {
|
|
63
|
+
durable: businessConfig.durable !== false,
|
|
64
|
+
arguments: { ...businessConfig.arguments }
|
|
65
|
+
};
|
|
66
|
+
} else {
|
|
67
|
+
// Fallback to provided options
|
|
68
|
+
queueOptions = this._buildQueueOptions(queueName, options);
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// If config not found, fall back to provided options
|
|
72
|
+
console.warn(`[QueueManager] Business queue config not found for ${queueName}, using provided options:`, error.message);
|
|
73
|
+
queueOptions = this._buildQueueOptions(queueName, options);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
// Unknown queue type - use provided options
|
|
77
|
+
queueOptions = this._buildQueueOptions(queueName, options);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Track managed queue
|
|
81
|
+
this.managedQueues.add(queueName);
|
|
82
|
+
|
|
83
|
+
// Assert the queue
|
|
84
|
+
return channel.assertQueue(queueName, queueOptions);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Build queue options from provided configuration
|
|
89
|
+
* @private
|
|
90
|
+
*/
|
|
91
|
+
_buildQueueOptions(queueName, options) {
|
|
33
92
|
const queueOptions = {
|
|
34
93
|
durable: options.durable !== false,
|
|
35
94
|
arguments: {
|
|
@@ -63,11 +122,21 @@ class QueueManager {
|
|
|
63
122
|
queueOptions.arguments['x-max-priority'] = options.maxPriority;
|
|
64
123
|
}
|
|
65
124
|
|
|
66
|
-
|
|
67
|
-
|
|
125
|
+
return queueOptions;
|
|
126
|
+
}
|
|
68
127
|
|
|
69
|
-
|
|
70
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Ensure infrastructure queue exists (workflow.*, registry.*)
|
|
130
|
+
* Uses central configuration from queueConfig
|
|
131
|
+
*
|
|
132
|
+
* @param {string} queueName - Infrastructure queue name (e.g., 'workflow.init', 'registry.register')
|
|
133
|
+
* @returns {Promise<Object>} Queue assertion result
|
|
134
|
+
*/
|
|
135
|
+
async ensureInfrastructureQueue(queueName) {
|
|
136
|
+
if (!queueConfig.isInfrastructureQueue(queueName)) {
|
|
137
|
+
throw new Error(`Queue ${queueName} is not an infrastructure queue. Use ensureQueue() for business queues.`);
|
|
138
|
+
}
|
|
139
|
+
return this.ensureQueue(queueName);
|
|
71
140
|
}
|
|
72
141
|
|
|
73
142
|
/**
|
package/src/layers/RPCHandler.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const amqp = require('amqplib');
|
|
9
9
|
const EventEmitter = require('events');
|
|
10
|
+
const queueConfig = require('../config/queueConfig');
|
|
10
11
|
|
|
11
12
|
class RabbitMQClient extends EventEmitter {
|
|
12
13
|
/**
|
|
@@ -121,7 +122,9 @@ class RabbitMQClient extends EventEmitter {
|
|
|
121
122
|
try {
|
|
122
123
|
// Ensure queue exists if publishing directly to queue and using default exchange
|
|
123
124
|
if (!exchange) {
|
|
124
|
-
|
|
125
|
+
// Use central config for infrastructure queues
|
|
126
|
+
const queueOptions = this._getQueueOptions(queue);
|
|
127
|
+
await this._channel.assertQueue(queue, queueOptions);
|
|
125
128
|
this._channel.sendToQueue(queue, buffer, { persistent, headers, routingKey });
|
|
126
129
|
} else {
|
|
127
130
|
// If exchange is specified, assert exchange and publish to it
|
|
@@ -157,8 +160,24 @@ class RabbitMQClient extends EventEmitter {
|
|
|
157
160
|
// Skip assertQueue for reply queues (they're already created with specific settings)
|
|
158
161
|
// Reply queues start with 'rpc.reply.' and are created as non-durable
|
|
159
162
|
if (!queue.startsWith('rpc.reply.')) {
|
|
160
|
-
//
|
|
161
|
-
|
|
163
|
+
// Use central config for infrastructure queues
|
|
164
|
+
const queueOptions = this._getQueueOptions(queue, { durable });
|
|
165
|
+
|
|
166
|
+
// Try to check if queue exists first (to avoid PRECONDITION-FAILED if it exists with different args)
|
|
167
|
+
try {
|
|
168
|
+
await this._channel.checkQueue(queue);
|
|
169
|
+
// Queue exists - use it as-is (don't try to assert with different args)
|
|
170
|
+
// This prevents 406 PRECONDITION-FAILED errors
|
|
171
|
+
} catch (checkErr) {
|
|
172
|
+
// Queue doesn't exist (404) - create it with our config
|
|
173
|
+
if (checkErr.code === 404) {
|
|
174
|
+
await this._channel.assertQueue(queue, queueOptions);
|
|
175
|
+
} else {
|
|
176
|
+
// Other error (e.g., 406) - queue exists with different args, use it as-is
|
|
177
|
+
// Log warning but proceed to consume
|
|
178
|
+
console.warn(`[RabbitMQClient] Queue ${queue} exists with different arguments, using as-is:`, checkErr.message);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
162
181
|
}
|
|
163
182
|
// Set prefetch if provided
|
|
164
183
|
if (typeof prefetch === 'number') {
|
|
@@ -189,6 +208,56 @@ class RabbitMQClient extends EventEmitter {
|
|
|
189
208
|
}
|
|
190
209
|
}
|
|
191
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Get queue options - uses central config for infrastructure and business queues
|
|
213
|
+
* @private
|
|
214
|
+
* @param {string} queueName - Queue name
|
|
215
|
+
* @param {Object} [defaultOptions] - Default options if config not found
|
|
216
|
+
* @returns {Object} Queue options
|
|
217
|
+
*/
|
|
218
|
+
_getQueueOptions(queueName, defaultOptions = {}) {
|
|
219
|
+
// Check if this is an infrastructure queue - use central config
|
|
220
|
+
if (queueConfig.isInfrastructureQueue(queueName)) {
|
|
221
|
+
try {
|
|
222
|
+
const infraConfig = queueConfig.getInfrastructureQueueConfig(queueName);
|
|
223
|
+
return {
|
|
224
|
+
durable: infraConfig.durable !== false,
|
|
225
|
+
arguments: { ...infraConfig.arguments }
|
|
226
|
+
};
|
|
227
|
+
} catch (error) {
|
|
228
|
+
// If config not found, fall back to default options
|
|
229
|
+
console.warn(`[RabbitMQClient] Infrastructure queue config not found for ${queueName}, using default options:`, error.message);
|
|
230
|
+
return {
|
|
231
|
+
durable: defaultOptions.durable !== false,
|
|
232
|
+
...defaultOptions
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Check if this is a business queue - use central business queue config
|
|
238
|
+
if (queueConfig.isBusinessQueue(queueName)) {
|
|
239
|
+
try {
|
|
240
|
+
const parsed = queueConfig.parseBusinessQueue(queueName);
|
|
241
|
+
if (parsed) {
|
|
242
|
+
const businessConfig = queueConfig.getBusinessQueueConfig(parsed.queueType, parsed.serviceName);
|
|
243
|
+
return {
|
|
244
|
+
durable: businessConfig.durable !== false,
|
|
245
|
+
arguments: { ...businessConfig.arguments }
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
// If config not found, fall back to default options
|
|
250
|
+
console.warn(`[RabbitMQClient] Business queue config not found for ${queueName}, using default options:`, error.message);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Unknown queue type - use default options
|
|
255
|
+
return {
|
|
256
|
+
durable: defaultOptions.durable !== false,
|
|
257
|
+
...defaultOptions
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
192
261
|
/**
|
|
193
262
|
* Acknowledges a message.
|
|
194
263
|
* @param {Object} msg - RabbitMQ message object.
|
|
File without changes
|
|
File without changes
|
package/src/utils/logger.js
CHANGED
|
File without changes
|
package/src/utils/serializer.js
CHANGED
|
File without changes
|