@diia-inhouse/diia-queue 13.3.4 → 14.0.10

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.
Files changed (148) hide show
  1. package/dist/constants.js +9 -9
  2. package/dist/index.d.ts +27 -0
  3. package/dist/index.js +23 -21
  4. package/dist/interfaces/deps.d.ts +30 -0
  5. package/dist/interfaces/externalCommunicator.d.ts +58 -0
  6. package/dist/interfaces/index.d.ts +95 -0
  7. package/dist/interfaces/index.js +7 -30
  8. package/dist/interfaces/messageBrokerServiceConfig.d.ts +80 -0
  9. package/dist/interfaces/messageBrokerServiceConfig.js +27 -24
  10. package/dist/interfaces/messageHandler.d.ts +6 -0
  11. package/dist/interfaces/metrics/index.d.ts +23 -0
  12. package/dist/interfaces/metrics/index.js +12 -16
  13. package/dist/interfaces/options.d.ts +52 -0
  14. package/dist/interfaces/providers/rabbitmq/amqpConnection.d.ts +33 -0
  15. package/dist/interfaces/providers/rabbitmq/amqpConnection.js +14 -17
  16. package/dist/interfaces/providers/rabbitmq/amqpPublisher.d.ts +20 -0
  17. package/dist/interfaces/providers/rabbitmq/index.d.ts +107 -0
  18. package/dist/interfaces/providers/rabbitmq/index.js +35 -47
  19. package/dist/interfaces/queueConfig/configs.d.ts +47 -0
  20. package/dist/interfaces/queueConfig/configs.js +8 -9
  21. package/dist/interfaces/queueConfig/index.d.ts +1 -0
  22. package/dist/interfaces/queueContext.d.ts +8 -0
  23. package/dist/interfaces/queueStatus.d.ts +15 -0
  24. package/dist/interfaces/queueStatus.js +8 -9
  25. package/dist/metrics/index.js +5 -8
  26. package/dist/providers/index.d.ts +5 -0
  27. package/dist/providers/index.js +6 -22
  28. package/dist/providers/rabbitmq/amqpAsserter.d.ts +53 -0
  29. package/dist/providers/rabbitmq/amqpAsserter.js +369 -416
  30. package/dist/providers/rabbitmq/amqpConnection.d.ts +24 -0
  31. package/dist/providers/rabbitmq/amqpConnection.js +97 -150
  32. package/dist/providers/rabbitmq/amqpListener.d.ts +47 -0
  33. package/dist/providers/rabbitmq/amqpListener.js +218 -225
  34. package/dist/providers/rabbitmq/amqpPublisher.d.ts +38 -0
  35. package/dist/providers/rabbitmq/amqpPublisher.js +184 -191
  36. package/dist/providers/rabbitmq/index.d.ts +56 -0
  37. package/dist/providers/rabbitmq/index.js +161 -186
  38. package/dist/services/communicator.d.ts +66 -0
  39. package/dist/services/communicator.js +164 -186
  40. package/dist/services/eventBus.d.ts +25 -0
  41. package/dist/services/eventBus.js +45 -57
  42. package/dist/services/eventCommunicator.js +75 -124
  43. package/dist/services/eventMessageHandler.d.ts +25 -0
  44. package/dist/services/eventMessageHandler.js +112 -129
  45. package/dist/services/eventMessageValidator.d.ts +13 -0
  46. package/dist/services/eventMessageValidator.js +59 -44
  47. package/dist/services/externalCommunicator.d.ts +114 -0
  48. package/dist/services/externalCommunicator.js +149 -140
  49. package/dist/services/externalEventBus.d.ts +39 -0
  50. package/dist/services/externalEventBus.js +144 -162
  51. package/dist/services/index.d.ts +8 -0
  52. package/dist/services/index.js +10 -26
  53. package/dist/services/metrics.d.ts +15 -0
  54. package/dist/services/metrics.js +46 -49
  55. package/dist/services/optionsBuilder.d.ts +14 -0
  56. package/dist/services/optionsBuilder.js +43 -58
  57. package/dist/services/queue.d.ts +29 -0
  58. package/dist/services/queue.js +78 -93
  59. package/dist/services/scheduledTask.d.ts +30 -0
  60. package/dist/services/scheduledTask.js +60 -68
  61. package/dist/services/task.d.ts +33 -0
  62. package/dist/services/task.js +160 -176
  63. package/dist/utils.js +7 -11
  64. package/package.json +44 -50
  65. package/dist/constants.js.map +0 -1
  66. package/dist/index.js.map +0 -1
  67. package/dist/interfaces/deps.js +0 -3
  68. package/dist/interfaces/deps.js.map +0 -1
  69. package/dist/interfaces/externalCommunicator.js +0 -3
  70. package/dist/interfaces/externalCommunicator.js.map +0 -1
  71. package/dist/interfaces/index.js.map +0 -1
  72. package/dist/interfaces/messageBrokerServiceConfig.js.map +0 -1
  73. package/dist/interfaces/messageHandler.js +0 -3
  74. package/dist/interfaces/messageHandler.js.map +0 -1
  75. package/dist/interfaces/metrics/index.js.map +0 -1
  76. package/dist/interfaces/options.js +0 -3
  77. package/dist/interfaces/options.js.map +0 -1
  78. package/dist/interfaces/providers/rabbitmq/amqpConnection.js.map +0 -1
  79. package/dist/interfaces/providers/rabbitmq/amqpPublisher.js +0 -4
  80. package/dist/interfaces/providers/rabbitmq/amqpPublisher.js.map +0 -1
  81. package/dist/interfaces/providers/rabbitmq/index.js.map +0 -1
  82. package/dist/interfaces/queueConfig/configs.js.map +0 -1
  83. package/dist/interfaces/queueConfig/index.js +0 -18
  84. package/dist/interfaces/queueConfig/index.js.map +0 -1
  85. package/dist/interfaces/queueContext.js +0 -3
  86. package/dist/interfaces/queueContext.js.map +0 -1
  87. package/dist/interfaces/queueStatus.js.map +0 -1
  88. package/dist/interfaces/services/eventMessageHandler.js +0 -3
  89. package/dist/interfaces/services/eventMessageHandler.js.map +0 -1
  90. package/dist/metrics/index.js.map +0 -1
  91. package/dist/providers/index.js.map +0 -1
  92. package/dist/providers/rabbitmq/amqpAsserter.js.map +0 -1
  93. package/dist/providers/rabbitmq/amqpConnection.js.map +0 -1
  94. package/dist/providers/rabbitmq/amqpListener.js.map +0 -1
  95. package/dist/providers/rabbitmq/amqpPublisher.js.map +0 -1
  96. package/dist/providers/rabbitmq/index.js.map +0 -1
  97. package/dist/services/communicator.js.map +0 -1
  98. package/dist/services/eventBus.js.map +0 -1
  99. package/dist/services/eventCommunicator.js.map +0 -1
  100. package/dist/services/eventMessageHandler.js.map +0 -1
  101. package/dist/services/eventMessageValidator.js.map +0 -1
  102. package/dist/services/externalCommunicator.js.map +0 -1
  103. package/dist/services/externalEventBus.js.map +0 -1
  104. package/dist/services/index.js.map +0 -1
  105. package/dist/services/metrics.js.map +0 -1
  106. package/dist/services/optionsBuilder.js.map +0 -1
  107. package/dist/services/queue.js.map +0 -1
  108. package/dist/services/scheduledTask.js.map +0 -1
  109. package/dist/services/task.js.map +0 -1
  110. package/dist/types/constants.d.ts +0 -8
  111. package/dist/types/index.d.ts +0 -4
  112. package/dist/types/interfaces/deps.d.ts +0 -26
  113. package/dist/types/interfaces/externalCommunicator.d.ts +0 -54
  114. package/dist/types/interfaces/index.d.ts +0 -99
  115. package/dist/types/interfaces/messageBrokerServiceConfig.d.ts +0 -79
  116. package/dist/types/interfaces/messageHandler.d.ts +0 -2
  117. package/dist/types/interfaces/metrics/index.d.ts +0 -20
  118. package/dist/types/interfaces/options.d.ts +0 -49
  119. package/dist/types/interfaces/providers/rabbitmq/amqpConnection.d.ts +0 -29
  120. package/dist/types/interfaces/providers/rabbitmq/amqpPublisher.d.ts +0 -16
  121. package/dist/types/interfaces/providers/rabbitmq/index.d.ts +0 -114
  122. package/dist/types/interfaces/queueConfig/configs.d.ts +0 -47
  123. package/dist/types/interfaces/queueConfig/index.d.ts +0 -1
  124. package/dist/types/interfaces/queueContext.d.ts +0 -4
  125. package/dist/types/interfaces/queueStatus.d.ts +0 -11
  126. package/dist/types/interfaces/services/eventMessageHandler.d.ts +0 -5
  127. package/dist/types/metrics/index.d.ts +0 -3
  128. package/dist/types/providers/index.d.ts +0 -5
  129. package/dist/types/providers/rabbitmq/amqpAsserter.d.ts +0 -49
  130. package/dist/types/providers/rabbitmq/amqpConnection.d.ts +0 -20
  131. package/dist/types/providers/rabbitmq/amqpListener.d.ts +0 -42
  132. package/dist/types/providers/rabbitmq/amqpPublisher.d.ts +0 -34
  133. package/dist/types/providers/rabbitmq/index.d.ts +0 -52
  134. package/dist/types/services/communicator.d.ts +0 -57
  135. package/dist/types/services/eventBus.d.ts +0 -20
  136. package/dist/types/services/eventCommunicator.d.ts +0 -15
  137. package/dist/types/services/eventMessageHandler.d.ts +0 -19
  138. package/dist/types/services/eventMessageValidator.d.ts +0 -9
  139. package/dist/types/services/externalCommunicator.d.ts +0 -110
  140. package/dist/types/services/externalEventBus.d.ts +0 -33
  141. package/dist/types/services/index.d.ts +0 -9
  142. package/dist/types/services/metrics.d.ts +0 -11
  143. package/dist/types/services/optionsBuilder.d.ts +0 -10
  144. package/dist/types/services/queue.d.ts +0 -23
  145. package/dist/types/services/scheduledTask.d.ts +0 -25
  146. package/dist/types/services/task.d.ts +0 -28
  147. package/dist/types/utils.d.ts +0 -3
  148. package/dist/utils.js.map +0 -1
@@ -1,417 +1,370 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
1
+ import { Arguments } from "../../interfaces/providers/rabbitmq/index.js";
2
+ import { AmqpConnectionEventNames } from "../../interfaces/providers/rabbitmq/amqpConnection.js";
3
+ import { QueueTypes } from "../../interfaces/messageBrokerServiceConfig.js";
4
+ import "../../interfaces/index.js";
5
+ import constants_default from "../../constants.js";
6
+ import { readFile } from "node:fs/promises";
7
+ //#region src/providers/rabbitmq/amqpAsserter.ts
8
+ var AmqpAsserter = class AmqpAsserter {
9
+ connection;
10
+ logger;
11
+ declareOptions;
12
+ static PrefixAlternate = "Alternate";
13
+ defaultExchangeType = "topic";
14
+ defaultQueueOptions = { durable: true };
15
+ defaultExchangeOptions = {
16
+ durable: true,
17
+ autoDelete: false
18
+ };
19
+ defaultDeclareOptions = {
20
+ assertQueues: true,
21
+ assertExchanges: true
22
+ };
23
+ channel;
24
+ channelName = "asserterChannel";
25
+ constructor(connection, logger, declareOptions = {}) {
26
+ this.connection = connection;
27
+ this.logger = logger;
28
+ this.declareOptions = declareOptions;
29
+ declareOptions.assertQueues ??= this.defaultDeclareOptions.assertQueues;
30
+ declareOptions.assertExchanges ??= this.defaultDeclareOptions.assertExchanges;
31
+ }
32
+ async init(exchangesOptions = [], queuesOptions = []) {
33
+ if (!this.channel) {
34
+ await this.initChannel();
35
+ this.connection.on(AmqpConnectionEventNames.Ready, async () => {
36
+ await this.initChannel();
37
+ });
38
+ }
39
+ await this.assertExchanges(exchangesOptions);
40
+ await this.declareQueues(queuesOptions, exchangesOptions);
41
+ }
42
+ async declareQueuesByJSON(declarationConfigPath) {
43
+ const { exchangesOptions, queuesOptions } = await this.readConfig(declarationConfigPath);
44
+ await this.declareQueues(queuesOptions, exchangesOptions);
45
+ }
46
+ async declareQueues(queuesOptions = [], exchangesOptions = []) {
47
+ for (const queueOptions of queuesOptions) {
48
+ const { name: queueName, redeclareOptions: { redeclare } = {} } = queueOptions;
49
+ if (redeclare) try {
50
+ await this.redeclareQueue(queueOptions, exchangesOptions);
51
+ } catch (err) {
52
+ this.logger.error(`Error while redeclaring queue ${queueName}`, { err });
53
+ throw err;
54
+ }
55
+ else await this.assertQueue(queueOptions);
56
+ }
57
+ }
58
+ async redeclareQueue(queueOptions, exchangesOptions) {
59
+ const { bindTo = [], redeclareOptions = {} } = queueOptions;
60
+ const { redeclare } = redeclareOptions;
61
+ if (!redeclare) return false;
62
+ const redeclareQueueOptions = this.prepareRedeclareQueueOptions(queueOptions, redeclareOptions);
63
+ const { name: queueName } = redeclareQueueOptions;
64
+ this.logger.info(`Queue ${queueName} is being to be redeclared`, { options: redeclareQueueOptions });
65
+ const alternateExchangesOptions = await this.assertAlternateExchanges(redeclareQueueOptions, exchangesOptions);
66
+ const { name: alternateQueueName } = await this.assertAlternateQueue(redeclareQueueOptions, alternateExchangesOptions);
67
+ await this.unbindQueueFromExchanges({
68
+ bindTo: [],
69
+ name: queueName,
70
+ unbindFrom: bindTo.map(({ exchangeName, routingKey }) => ({
71
+ routingKey,
72
+ exchangeName,
73
+ unbind: true
74
+ }))
75
+ });
76
+ const { name: tempQueueName, bindTo: tempBindTo } = await this.assertTemporaryQueue(redeclareQueueOptions);
77
+ await this.transferMessageBetweenQueues(queueName, tempQueueName);
78
+ try {
79
+ await this.deleteQueue(queueName);
80
+ } catch (err) {
81
+ if (!(err instanceof Error)) throw err;
82
+ if (err.message.includes("NOT_FOUND - no queue")) {
83
+ await this.deleteQueues([alternateQueueName, tempQueueName]);
84
+ await this.deleteExchanges(Object.values(alternateExchangesOptions));
85
+ }
86
+ return false;
87
+ }
88
+ await this.assertQueue(redeclareQueueOptions);
89
+ await this.unbindQueueFromExchanges({
90
+ bindTo: [],
91
+ name: tempQueueName,
92
+ unbindFrom: tempBindTo.map(({ exchangeName, routingKey }) => ({
93
+ routingKey,
94
+ exchangeName,
95
+ unbind: true
96
+ }))
97
+ });
98
+ await this.transferMessageBetweenQueues(tempQueueName, queueName);
99
+ await this.transferMessageBetweenQueues(alternateQueueName, queueName);
100
+ await this.deleteQueue(tempQueueName);
101
+ await this.deleteQueue(alternateQueueName);
102
+ await this.deleteExchanges(Object.values(alternateExchangesOptions));
103
+ this.logger.info(`Queue ${queueName} has been redeclared successfully`);
104
+ return true;
105
+ }
106
+ async transferMessageBetweenQueues(sourceQueueName, destinationQueueName) {
107
+ this.logger.info(`Transferring messages from ${sourceQueueName} to ${destinationQueueName} is being started`);
108
+ let messageCount = 0;
109
+ const channel = await this.connection.createChannel();
110
+ while (true) {
111
+ let message;
112
+ try {
113
+ const gotMessage = await channel.get(sourceQueueName, { noAck: true });
114
+ if (!gotMessage) break;
115
+ message = gotMessage;
116
+ messageCount += 1;
117
+ } catch (err) {
118
+ this.logger.error(`Error while getting messages from ${sourceQueueName}`, {
119
+ err,
120
+ messageCount
121
+ });
122
+ throw err;
123
+ }
124
+ try {
125
+ channel.sendToQueue(destinationQueueName, message.content, message.properties);
126
+ this.logger.debug(`Message ${messageCount} has been transferred from ${sourceQueueName} to ${destinationQueueName}`);
127
+ } catch (err) {
128
+ this.logger.error(`Error while publishing messages to ${destinationQueueName}`, {
129
+ err,
130
+ messageCount
131
+ });
132
+ throw err;
133
+ }
134
+ }
135
+ await channel.close();
136
+ this.logger.info(`Transferring messages from ${sourceQueueName} to ${destinationQueueName} has been finished`, { messageCount });
137
+ }
138
+ async deleteExchanges(exchangesOptions) {
139
+ const result = {};
140
+ for (const exchangeOptions of exchangesOptions) {
141
+ const { name: exchangeName } = exchangeOptions;
142
+ result[exchangeName] = await this.deleteExchange(exchangeName);
143
+ }
144
+ return result;
145
+ }
146
+ async deleteExchange(exchangeName) {
147
+ try {
148
+ const result = await this.channel?.deleteExchange(exchangeName);
149
+ this.logger.info(`Exchange ${exchangeName} has been deleted`);
150
+ return result;
151
+ } catch (err) {
152
+ this.logger.error(`Error while deleting an exchange`, {
153
+ exchangeName,
154
+ err
155
+ });
156
+ throw err;
157
+ }
158
+ }
159
+ async deleteQueues(queueNames) {
160
+ const result = {};
161
+ for (const queueName of queueNames) result[queueName] = await this.deleteQueue(queueName);
162
+ return result;
163
+ }
164
+ async deleteQueue(queueName) {
165
+ try {
166
+ const result = await this.channel?.deleteQueue(queueName);
167
+ this.logger.info(`Queue ${queueName} has been deleted`, {
168
+ queueName,
169
+ result
170
+ });
171
+ return result;
172
+ } catch (err) {
173
+ this.logger.error(`Error while deleting a queue`, {
174
+ queueName,
175
+ err
176
+ });
177
+ throw err;
178
+ }
179
+ }
180
+ async assertQueues(queueOptions) {
181
+ const result = {};
182
+ for (const queueOption of queueOptions) result[queueOption.name] = await this.assertQueue(queueOption);
183
+ return result;
184
+ }
185
+ async assertQueue(queueOptions) {
186
+ const { bindTo, declare, name: queueName, unbindFrom = [], type = QueueTypes.Quorum, options = this.defaultQueueOptions } = queueOptions;
187
+ const shouldDeclare = declare ?? this.declareOptions.assertQueues;
188
+ options.arguments = {
189
+ ...options.arguments,
190
+ ...type === QueueTypes.Quorum ? { [Arguments.queueType]: type } : {}
191
+ };
192
+ try {
193
+ if (shouldDeclare) {
194
+ const result = await this.channel?.assertQueue(queueName, options);
195
+ this.logger.info(`Queue ${queueName} has been declared`, {
196
+ queueOptions,
197
+ result
198
+ });
199
+ for (const bindOptions of bindTo) await this.bindQueueToExchange(queueName, bindOptions);
200
+ for (const unbindOption of unbindFrom) await this.unbindQueueFromExchange(queueName, unbindOption);
201
+ return result;
202
+ }
203
+ } catch (err) {
204
+ this.logger.error(`Error while assert queue`, {
205
+ queueName,
206
+ err
207
+ });
208
+ throw err;
209
+ }
210
+ }
211
+ async assertExchanges(exchangeOptions) {
212
+ const result = {};
213
+ for (const exchangeOption of exchangeOptions) result[exchangeOption.name] = await this.assertExchange(exchangeOption);
214
+ return result;
215
+ }
216
+ async assertExchange(exchangeOptions) {
217
+ const { name, delayed, declare, bindTo = [], type = this.defaultExchangeType, options = this.defaultExchangeOptions } = exchangeOptions;
218
+ options.arguments = {
219
+ ...options.arguments,
220
+ ...delayed ? { [Arguments.delayedType]: "topic" } : {}
221
+ };
222
+ const exchangeType = delayed ? "x-delayed-message" : type;
223
+ const shouldDeclare = declare ?? this.declareOptions.assertExchanges;
224
+ try {
225
+ if (shouldDeclare) {
226
+ const result = await this.channel?.assertExchange(name, exchangeType, options);
227
+ this.logger.info(`Exchange ${name} has been declared`, {
228
+ exchangeOptions,
229
+ result
230
+ });
231
+ await this.bindExchangeToExchanges(name, bindTo);
232
+ return result;
233
+ }
234
+ } catch (err) {
235
+ this.logger.error(`Error while assert exchange`, {
236
+ name,
237
+ err
238
+ });
239
+ throw err;
240
+ }
241
+ }
242
+ async unbindQueueFromExchanges(queueOptions) {
243
+ const { name: queueName, unbindFrom = [] } = queueOptions;
244
+ for (const unbindOption of unbindFrom) await this.unbindQueueFromExchange(queueName, unbindOption);
245
+ }
246
+ async bindQueueToExchange(queueName, bindOptions) {
247
+ const { bind, exchangeName, routingKey = constants_default.DEFAULT_ROUTING_KEY } = bindOptions;
248
+ const shouldBind = bind ?? this.declareOptions.assertQueues;
249
+ try {
250
+ if (shouldBind) {
251
+ const result = await this.channel?.bindQueue(queueName, exchangeName, routingKey);
252
+ this.logger.info(`Queue [${queueName}] has been bound with exchange [${exchangeName}] by routing key [${routingKey}]`);
253
+ return result;
254
+ }
255
+ } catch (err) {
256
+ this.logger.error(`Error while binding queue [${queueName}] to exchange [${exchangeName}] by routing key [${routingKey}]`, { err });
257
+ throw err;
258
+ }
259
+ }
260
+ async bindExchangeToExchanges(exchangeName, bindTo) {
261
+ for (const bindOption of bindTo) await this.bindExchangeToExchange(exchangeName, bindOption);
262
+ }
263
+ async bindExchangeToExchange(exchangeName, bindOptions) {
264
+ const { bind, exchangeName: relatedExchangeName, routingKey = constants_default.DEFAULT_ROUTING_KEY } = bindOptions;
265
+ const shouldBind = bind ?? this.declareOptions.assertExchanges;
266
+ try {
267
+ if (shouldBind) {
268
+ const result = await this.channel?.bindExchange(exchangeName, relatedExchangeName, routingKey);
269
+ this.logger.info(`Exchange [${exchangeName}] has been bound with exchange [${relatedExchangeName}] by routing key [${routingKey}]`);
270
+ return result;
271
+ }
272
+ } catch (err) {
273
+ this.logger.error(`Error while binding exchange [${exchangeName}] to exchange [${relatedExchangeName}] by routing key [${routingKey}]`, { err });
274
+ throw err;
275
+ }
276
+ }
277
+ async unbindQueueFromExchange(queueName, unbindOptions) {
278
+ const { unbind, exchangeName, routingKey = constants_default.DEFAULT_ROUTING_KEY } = unbindOptions;
279
+ try {
280
+ if (unbind) {
281
+ const result = await this.channel?.unbindQueue(queueName, exchangeName, routingKey);
282
+ this.logger.info(`Queue [${queueName}] has been unbound from exchange [${exchangeName}] by routing key [${routingKey}]`);
283
+ return result;
284
+ }
285
+ } catch (err) {
286
+ this.logger.error(`Error while unbinding queue [${queueName}] from exchange [${exchangeName}] by routing key [${routingKey}]`, { err });
287
+ throw err;
288
+ }
289
+ }
290
+ async disconnect() {
291
+ await this.channel?.close();
292
+ this.connection.removeAllListeners(AmqpConnectionEventNames.Ready);
293
+ await this.connection.closeConnection();
294
+ }
295
+ async initChannel() {
296
+ this.channel = await this.connection.createChannel(this.channelName);
297
+ }
298
+ async readConfig(declarationConfigPath) {
299
+ const data = await readFile(declarationConfigPath, { encoding: "utf8" });
300
+ return JSON.parse(data);
301
+ }
302
+ async assertTemporaryQueue(queueOptions) {
303
+ const tempQueueOptions = this.prepareTemporaryQueueOptions(queueOptions);
304
+ await this.assertQueue(tempQueueOptions);
305
+ return tempQueueOptions;
306
+ }
307
+ async assertAlternateExchanges(queueOptions, exchangesOptions) {
308
+ const alternateExchangesOptions = this.prepareAlternateExchangesOptions(queueOptions, exchangesOptions);
309
+ await this.assertExchanges(Object.values(alternateExchangesOptions));
310
+ return alternateExchangesOptions;
311
+ }
312
+ async assertAlternateQueue(queueOptions, alternateExchangesOptions) {
313
+ const alternateQueueOptions = this.prepareAlternateQueueOptions(queueOptions, alternateExchangesOptions);
314
+ await this.assertQueue(alternateQueueOptions);
315
+ return alternateQueueOptions;
316
+ }
317
+ prepareRedeclareQueueOptions(queueOptions, redeclareQueueOptions) {
318
+ return {
319
+ ...queueOptions,
320
+ ...redeclareQueueOptions
321
+ };
322
+ }
323
+ prepareTemporaryQueueOptions(queueOptions) {
324
+ return {
325
+ ...queueOptions,
326
+ name: `${queueOptions.name}Temporary`
327
+ };
328
+ }
329
+ prepareAlternateExchangesOptions(queueOptions, exchangesOptions) {
330
+ const result = {};
331
+ if (exchangesOptions.length === 0) return result;
332
+ const exchangesOptionsMap = new Map(exchangesOptions.map((exchangeOptions) => [exchangeOptions.name, exchangeOptions]));
333
+ for (const bindOptions of queueOptions.bindTo) {
334
+ const { exchangeName, bind } = bindOptions;
335
+ if (!bind) continue;
336
+ const exchangeOptions = exchangesOptionsMap.get(exchangeName);
337
+ if (!exchangeOptions) throw new Error(`Alternate exchange options could not be defined for unknown exchange [${exchangeName}]`);
338
+ result[exchangeName] = this.prepareAlternateExchangeOptions(exchangeOptions);
339
+ }
340
+ return result;
341
+ }
342
+ prepareAlternateExchangeOptions(exchangeOptions) {
343
+ const { name: exchangeName } = exchangeOptions;
344
+ return {
345
+ ...exchangeOptions,
346
+ declare: true,
347
+ name: `${exchangeName}${AmqpAsserter.PrefixAlternate}`
348
+ };
349
+ }
350
+ prepareAlternateQueueOptions(queueOptions, alternateExchangesOptions) {
351
+ const { name: queueName, bindTo = [] } = queueOptions;
352
+ return {
353
+ ...queueOptions,
354
+ name: `${queueName}${AmqpAsserter.PrefixAlternate}`,
355
+ bindTo: this.prepareAlternateBindToQueueOption(bindTo, alternateExchangesOptions)
356
+ };
357
+ }
358
+ prepareAlternateBindToQueueOption(bindTo, alternateExchangesOptions) {
359
+ return bindTo.filter(({ exchangeName }) => exchangeName in alternateExchangesOptions).map(({ exchangeName, routingKey }) => {
360
+ const { name: alternateExchangeName } = alternateExchangesOptions[exchangeName];
361
+ return {
362
+ bind: true,
363
+ routingKey,
364
+ exchangeName: alternateExchangeName
365
+ };
366
+ });
367
+ }
4
368
  };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.AmqpAsserter = void 0;
7
- const promises_1 = require("node:fs/promises");
8
- const constants_1 = __importDefault(require("../../constants"));
9
- const interfaces_1 = require("../../interfaces");
10
- const messageBrokerServiceConfig_1 = require("../../interfaces/messageBrokerServiceConfig");
11
- class AmqpAsserter {
12
- connection;
13
- logger;
14
- declareOptions;
15
- static PrefixAlternate = 'Alternate';
16
- defaultExchangeType = messageBrokerServiceConfig_1.ExchangeType.Topic;
17
- defaultQueueOptions = { durable: true };
18
- defaultExchangeOptions = {
19
- durable: true,
20
- autoDelete: false,
21
- };
22
- defaultDeclareOptions = {
23
- assertQueues: true,
24
- assertExchanges: true,
25
- };
26
- channel;
27
- channelName = 'asserterChannel';
28
- constructor(connection, logger, declareOptions = {}) {
29
- this.connection = connection;
30
- this.logger = logger;
31
- this.declareOptions = declareOptions;
32
- declareOptions.assertQueues ??= this.defaultDeclareOptions.assertQueues;
33
- declareOptions.assertExchanges ??= this.defaultDeclareOptions.assertExchanges;
34
- }
35
- async init(exchangesOptions = [], queuesOptions = []) {
36
- if (!this.channel) {
37
- await this.initChannel();
38
- this.connection.on(interfaces_1.AmqpConnectionEventNames.Ready, async () => {
39
- // in case if reconnect happened
40
- await this.initChannel();
41
- });
42
- }
43
- await this.assertExchanges(exchangesOptions);
44
- await this.declareQueues(queuesOptions, exchangesOptions);
45
- }
46
- async declareQueuesByJSON(declarationConfigPath) {
47
- const { exchangesOptions, queuesOptions } = await this.readConfig(declarationConfigPath);
48
- await this.declareQueues(queuesOptions, exchangesOptions);
49
- }
50
- async declareQueues(queuesOptions = [], exchangesOptions = []) {
51
- for await (const queueOptions of queuesOptions) {
52
- const { name: queueName, redeclareOptions: { redeclare } = {} } = queueOptions;
53
- if (redeclare) {
54
- try {
55
- await this.redeclareQueue(queueOptions, exchangesOptions);
56
- }
57
- catch (err) {
58
- this.logger.error(`Error while redeclaring queue ${queueName}`, { err });
59
- throw err;
60
- }
61
- }
62
- else {
63
- await this.assertQueue(queueOptions);
64
- }
65
- }
66
- }
67
- async redeclareQueue(queueOptions, exchangesOptions) {
68
- const { bindTo = [], redeclareOptions = {} } = queueOptions;
69
- const { redeclare } = redeclareOptions;
70
- if (!redeclare) {
71
- return false;
72
- }
73
- // prepare redeclare queue options
74
- const redeclareQueueOptions = this.prepareRedeclareQueueOptions(queueOptions, redeclareOptions);
75
- const { name: queueName } = redeclareQueueOptions;
76
- this.logger.info(`Queue ${queueName} is being to be redeclared`, { options: redeclareQueueOptions });
77
- // assert alternate exchanges
78
- const alternateExchangesOptions = await this.assertAlternateExchanges(redeclareQueueOptions, exchangesOptions);
79
- // assert alternate queue
80
- const { name: alternateQueueName } = await this.assertAlternateQueue(redeclareQueueOptions, alternateExchangesOptions);
81
- // unbind old queue
82
- await this.unbindQueueFromExchanges({
83
- bindTo: [],
84
- name: queueName,
85
- unbindFrom: bindTo.map(({ exchangeName, routingKey }) => ({
86
- routingKey,
87
- exchangeName,
88
- unbind: true,
89
- })),
90
- });
91
- // assert temporary queue
92
- const { name: tempQueueName, bindTo: tempBindTo } = await this.assertTemporaryQueue(redeclareQueueOptions);
93
- // transfer messages from old queue to temporary queue
94
- await this.transferMessageBetweenQueues(queueName, tempQueueName);
95
- try {
96
- // delete old queue
97
- await this.deleteQueue(queueName);
98
- }
99
- catch (err) {
100
- if (!(err instanceof Error)) {
101
- throw err;
102
- }
103
- if (err.message.includes('NOT_FOUND - no queue')) {
104
- await this.deleteQueues([alternateQueueName, tempQueueName]);
105
- await this.deleteExchanges(Object.values(alternateExchangesOptions));
106
- }
107
- return false;
108
- }
109
- // assert new queue
110
- await this.assertQueue(redeclareQueueOptions);
111
- // unbind temp queue
112
- await this.unbindQueueFromExchanges({
113
- bindTo: [],
114
- name: tempQueueName,
115
- unbindFrom: tempBindTo.map(({ exchangeName, routingKey }) => ({
116
- routingKey,
117
- exchangeName,
118
- unbind: true,
119
- })),
120
- });
121
- // transfer messages from temporary queue to new queue
122
- await this.transferMessageBetweenQueues(tempQueueName, queueName);
123
- // transfer messages from alternate queue to new queue
124
- await this.transferMessageBetweenQueues(alternateQueueName, queueName);
125
- // delete temporary queue
126
- await this.deleteQueue(tempQueueName);
127
- // delete alternate queue
128
- await this.deleteQueue(alternateQueueName);
129
- // delete alternate exchanges
130
- await this.deleteExchanges(Object.values(alternateExchangesOptions));
131
- this.logger.info(`Queue ${queueName} has been redeclared successfully`);
132
- return true;
133
- }
134
- async transferMessageBetweenQueues(sourceQueueName, destinationQueueName) {
135
- this.logger.info(`Transferring messages from ${sourceQueueName} to ${destinationQueueName} is being started`);
136
- let messageCount = 0;
137
- const channel = await this.connection.createChannel();
138
- while (true) {
139
- let message;
140
- try {
141
- const gotMessage = await channel.get(sourceQueueName, { noAck: true });
142
- if (!gotMessage) {
143
- break;
144
- }
145
- message = gotMessage;
146
- messageCount += 1;
147
- }
148
- catch (err) {
149
- this.logger.error(`Error while getting messages from ${sourceQueueName}`, { err, messageCount });
150
- throw err;
151
- }
152
- try {
153
- channel.sendToQueue(destinationQueueName, message.content, message.properties);
154
- this.logger.debug(`Message ${messageCount} has been transferred from ${sourceQueueName} to ${destinationQueueName}`);
155
- }
156
- catch (err) {
157
- this.logger.error(`Error while publishing messages to ${destinationQueueName}`, { err, messageCount });
158
- throw err;
159
- }
160
- }
161
- await channel.close();
162
- this.logger.info(`Transferring messages from ${sourceQueueName} to ${destinationQueueName} has been finished`, {
163
- messageCount,
164
- });
165
- }
166
- async deleteExchanges(exchangesOptions) {
167
- const result = {};
168
- for await (const exchangeOptions of exchangesOptions) {
169
- const { name: exchangeName } = exchangeOptions;
170
- result[exchangeName] = await this.deleteExchange(exchangeName);
171
- }
172
- return result;
173
- }
174
- async deleteExchange(exchangeName) {
175
- try {
176
- const result = await this.channel?.deleteExchange(exchangeName);
177
- this.logger.info(`Exchange ${exchangeName} has been deleted`);
178
- return result;
179
- }
180
- catch (err) {
181
- this.logger.error(`Error while deleting an exchange`, { exchangeName, err });
182
- throw err;
183
- }
184
- }
185
- async deleteQueues(queueNames) {
186
- const result = {};
187
- for (const queueName of queueNames) {
188
- result[queueName] = await this.deleteQueue(queueName);
189
- }
190
- return result;
191
- }
192
- async deleteQueue(queueName) {
193
- try {
194
- const result = await this.channel?.deleteQueue(queueName);
195
- this.logger.info(`Queue ${queueName} has been deleted`, { queueName, result });
196
- return result;
197
- }
198
- catch (err) {
199
- this.logger.error(`Error while deleting a queue`, { queueName, err });
200
- throw err;
201
- }
202
- }
203
- async assertQueues(queueOptions) {
204
- const result = {};
205
- for await (const queueOption of queueOptions) {
206
- result[queueOption.name] = await this.assertQueue(queueOption);
207
- }
208
- return result;
209
- }
210
- async assertQueue(queueOptions) {
211
- const { bindTo, declare, name: queueName, unbindFrom = [], type = messageBrokerServiceConfig_1.QueueTypes.Quorum, options = this.defaultQueueOptions, } = queueOptions;
212
- const shouldDeclare = declare ?? this.declareOptions.assertQueues;
213
- options.arguments = {
214
- ...options.arguments,
215
- ...(type === messageBrokerServiceConfig_1.QueueTypes.Quorum
216
- ? {
217
- [interfaces_1.Arguments.queueType]: type,
218
- }
219
- : {}),
220
- };
221
- try {
222
- if (shouldDeclare) {
223
- const result = await this.channel?.assertQueue(queueName, options);
224
- this.logger.info(`Queue ${queueName} has been declared`, { queueOptions, result });
225
- for await (const bindOptions of bindTo) {
226
- await this.bindQueueToExchange(queueName, bindOptions);
227
- }
228
- for await (const unbindOption of unbindFrom) {
229
- await this.unbindQueueFromExchange(queueName, unbindOption);
230
- }
231
- return result;
232
- }
233
- }
234
- catch (err) {
235
- this.logger.error(`Error while assert queue`, { queueName, err });
236
- throw err;
237
- }
238
- }
239
- async assertExchanges(exchangeOptions) {
240
- const result = {};
241
- for await (const exchangeOption of exchangeOptions) {
242
- result[exchangeOption.name] = await this.assertExchange(exchangeOption);
243
- }
244
- return result;
245
- }
246
- async assertExchange(exchangeOptions) {
247
- const { name, delayed, declare, bindTo = [], type = this.defaultExchangeType, options = this.defaultExchangeOptions, } = exchangeOptions;
248
- options.arguments = {
249
- ...options.arguments,
250
- ...(delayed ? { [interfaces_1.Arguments.delayedType]: messageBrokerServiceConfig_1.ExchangeType.Topic } : {}),
251
- };
252
- const exchangeType = delayed ? messageBrokerServiceConfig_1.ExchangeType.XDelayedMessage : type;
253
- const shouldDeclare = declare ?? this.declareOptions.assertExchanges;
254
- try {
255
- if (shouldDeclare) {
256
- const result = await this.channel?.assertExchange(name, exchangeType, options);
257
- this.logger.info(`Exchange ${name} has been declared`, { exchangeOptions, result });
258
- await this.bindExchangeToExchanges(name, bindTo);
259
- return result;
260
- }
261
- }
262
- catch (err) {
263
- this.logger.error(`Error while assert exchange`, { name, err });
264
- throw err;
265
- }
266
- }
267
- async unbindQueueFromExchanges(queueOptions) {
268
- const { name: queueName, unbindFrom = [] } = queueOptions;
269
- for await (const unbindOption of unbindFrom) {
270
- await this.unbindQueueFromExchange(queueName, unbindOption);
271
- }
272
- }
273
- async bindQueueToExchange(queueName, bindOptions) {
274
- const { bind, exchangeName, routingKey = constants_1.default.DEFAULT_ROUTING_KEY } = bindOptions;
275
- const shouldBind = bind ?? this.declareOptions.assertQueues;
276
- try {
277
- if (shouldBind) {
278
- const result = await this.channel?.bindQueue(queueName, exchangeName, routingKey);
279
- this.logger.info(`Queue [${queueName}] has been bound with exchange [${exchangeName}] by routing key [${routingKey}]`);
280
- return result;
281
- }
282
- }
283
- catch (err) {
284
- this.logger.error(`Error while binding queue [${queueName}] to exchange [${exchangeName}] by routing key [${routingKey}]`, {
285
- err,
286
- });
287
- throw err;
288
- }
289
- }
290
- async bindExchangeToExchanges(exchangeName, bindTo) {
291
- for await (const bindOption of bindTo) {
292
- await this.bindExchangeToExchange(exchangeName, bindOption);
293
- }
294
- }
295
- async bindExchangeToExchange(exchangeName, bindOptions) {
296
- const { bind, exchangeName: relatedExchangeName, routingKey = constants_1.default.DEFAULT_ROUTING_KEY } = bindOptions;
297
- const shouldBind = bind ?? this.declareOptions.assertExchanges;
298
- try {
299
- if (shouldBind) {
300
- const result = await this.channel?.bindExchange(exchangeName, relatedExchangeName, routingKey);
301
- this.logger.info(`Exchange [${exchangeName}] has been bound with exchange [${relatedExchangeName}] by routing key [${routingKey}]`);
302
- return result;
303
- }
304
- }
305
- catch (err) {
306
- this.logger.error(`Error while binding exchange [${exchangeName}] to exchange [${relatedExchangeName}] by routing key [${routingKey}]`, {
307
- err,
308
- });
309
- throw err;
310
- }
311
- }
312
- async unbindQueueFromExchange(queueName, unbindOptions) {
313
- const { unbind, exchangeName, routingKey = constants_1.default.DEFAULT_ROUTING_KEY } = unbindOptions;
314
- try {
315
- if (unbind) {
316
- const result = await this.channel?.unbindQueue(queueName, exchangeName, routingKey);
317
- this.logger.info(`Queue [${queueName}] has been unbound from exchange [${exchangeName}] by routing key [${routingKey}]`);
318
- return result;
319
- }
320
- }
321
- catch (err) {
322
- this.logger.error(`Error while unbinding queue [${queueName}] from exchange [${exchangeName}] by routing key [${routingKey}]`, {
323
- err,
324
- });
325
- throw err;
326
- }
327
- }
328
- async disconnect() {
329
- await this.channel?.close();
330
- this.connection.removeAllListeners(interfaces_1.AmqpConnectionEventNames.Ready);
331
- await this.connection.closeConnection();
332
- }
333
- async initChannel() {
334
- this.channel = await this.connection.createChannel(this.channelName);
335
- }
336
- async readConfig(declarationConfigPath) {
337
- // eslint-disable-next-line security/detect-non-literal-fs-filename
338
- const data = await (0, promises_1.readFile)(declarationConfigPath, { encoding: 'utf8' }); // nosemgrep: eslint.detect-non-literal-fs-filename
339
- return JSON.parse(data);
340
- }
341
- async assertTemporaryQueue(queueOptions) {
342
- const tempQueueOptions = this.prepareTemporaryQueueOptions(queueOptions);
343
- await this.assertQueue(tempQueueOptions);
344
- return tempQueueOptions;
345
- }
346
- async assertAlternateExchanges(queueOptions, exchangesOptions) {
347
- const alternateExchangesOptions = this.prepareAlternateExchangesOptions(queueOptions, exchangesOptions);
348
- await this.assertExchanges(Object.values(alternateExchangesOptions));
349
- return alternateExchangesOptions;
350
- }
351
- async assertAlternateQueue(queueOptions, alternateExchangesOptions) {
352
- const alternateQueueOptions = this.prepareAlternateQueueOptions(queueOptions, alternateExchangesOptions);
353
- await this.assertQueue(alternateQueueOptions);
354
- return alternateQueueOptions;
355
- }
356
- prepareRedeclareQueueOptions(queueOptions, redeclareQueueOptions) {
357
- return {
358
- ...queueOptions,
359
- ...redeclareQueueOptions,
360
- };
361
- }
362
- prepareTemporaryQueueOptions(queueOptions) {
363
- return {
364
- ...queueOptions,
365
- name: `${queueOptions.name}Temporary`,
366
- };
367
- }
368
- prepareAlternateExchangesOptions(queueOptions, exchangesOptions) {
369
- const result = {};
370
- if (exchangesOptions.length === 0) {
371
- return result;
372
- }
373
- const exchangesOptionsMap = new Map(exchangesOptions.map((exchangeOptions) => [exchangeOptions.name, exchangeOptions]));
374
- for (const bindOptions of queueOptions.bindTo) {
375
- const { exchangeName, bind } = bindOptions;
376
- if (!bind) {
377
- continue;
378
- }
379
- const exchangeOptions = exchangesOptionsMap.get(exchangeName);
380
- if (!exchangeOptions) {
381
- throw new Error(`Alternate exchange options could not be defined for unknown exchange [${exchangeName}]`);
382
- }
383
- result[exchangeName] = this.prepareAlternateExchangeOptions(exchangeOptions);
384
- }
385
- return result;
386
- }
387
- prepareAlternateExchangeOptions(exchangeOptions) {
388
- const { name: exchangeName } = exchangeOptions;
389
- return {
390
- ...exchangeOptions,
391
- declare: true,
392
- name: `${exchangeName}${AmqpAsserter.PrefixAlternate}`,
393
- };
394
- }
395
- prepareAlternateQueueOptions(queueOptions, alternateExchangesOptions) {
396
- const { name: queueName, bindTo = [] } = queueOptions;
397
- return {
398
- ...queueOptions,
399
- name: `${queueName}${AmqpAsserter.PrefixAlternate}`,
400
- bindTo: this.prepareAlternateBindToQueueOption(bindTo, alternateExchangesOptions),
401
- };
402
- }
403
- prepareAlternateBindToQueueOption(bindTo, alternateExchangesOptions) {
404
- return bindTo
405
- .filter(({ exchangeName }) => exchangeName in alternateExchangesOptions)
406
- .map(({ exchangeName, routingKey }) => {
407
- const { name: alternateExchangeName } = alternateExchangesOptions[exchangeName];
408
- return {
409
- bind: true,
410
- routingKey,
411
- exchangeName: alternateExchangeName,
412
- };
413
- });
414
- }
415
- }
416
- exports.AmqpAsserter = AmqpAsserter;
417
- //# sourceMappingURL=amqpAsserter.js.map
369
+ //#endregion
370
+ export { AmqpAsserter };