@diia-inhouse/diia-queue 13.3.3 → 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,225 +1,218 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AmqpListener = void 0;
4
- const errors_1 = require("@diia-inhouse/errors");
5
- const interfaces_1 = require("../../interfaces");
6
- const rabbitmq_1 = require("../../interfaces/providers/rabbitmq");
7
- const metrics_1 = require("../../metrics");
8
- class AmqpListener {
9
- connection;
10
- logger;
11
- rabbitMQMetrics;
12
- queuesOptions;
13
- systemServiceName;
14
- infiniteRecreateChannelTriesCount = 0;
15
- defaultRecreateChannelOptions = {
16
- maxTries: 6,
17
- timeout: 1000,
18
- backoffCoefficient: 2,
19
- };
20
- defaultPrefetchCount = 1;
21
- queuesChannelsMap = new Map();
22
- queueRecreateChannelTriesMap = new Map();
23
- defaultNackOptions = new interfaces_1.NackOptions(true, false);
24
- queuesCallbacksMap = new Map();
25
- queueConsumerOptionsMap = new Map();
26
- queueConsumerTagsMap = new Map();
27
- // in case when we receive a null message, we don't know how-to ack it, and emulate this message.
28
- // nothing changes cause previously this case had not been handled
29
- nullMessage = { fields: { deliveryTag: 'unknown' } };
30
- constructor(connection, logger, rabbitMQMetrics, queuesOptions = [], systemServiceName) {
31
- this.connection = connection;
32
- this.logger = logger;
33
- this.rabbitMQMetrics = rabbitMQMetrics;
34
- this.queuesOptions = queuesOptions;
35
- this.systemServiceName = systemServiceName;
36
- for (const queueOptions of this.queuesOptions) {
37
- const { name: queueName, consumerOptions: { prefetchCount, ...consumerOpts } = {} } = queueOptions;
38
- this.queueRecreateChannelTriesMap.set(queueName, 0);
39
- this.queueConsumerOptionsMap.set(queueName, { ...consumerOpts, prefetchCount: prefetchCount ?? this.defaultPrefetchCount });
40
- }
41
- }
42
- async init() {
43
- this.connection.on('ready', async () => {
44
- // in case if reconnect happened
45
- const tasks = Array.from(this.queuesChannelsMap.keys()).map(async (queueName) => {
46
- await this.createChannelAndListenQueue(queueName);
47
- });
48
- await Promise.all(tasks);
49
- });
50
- }
51
- async cancelQueue(queueName) {
52
- const channel = this.getQueueChannel(queueName);
53
- const consumerTags = this.queueConsumerTagsMap.get(queueName) || [];
54
- for await (const consumerTag of consumerTags) {
55
- await channel?.cancel(consumerTag);
56
- }
57
- }
58
- async listenQueue(queueName, callback) {
59
- this.logger.debug(`Start listen queue [${queueName}]`);
60
- try {
61
- this.saveQueueCallback(queueName, callback);
62
- await this.createChannelAndListenQueue(queueName);
63
- }
64
- catch (err) {
65
- this.logger.error(`Error while start listen queue [${queueName}]`, { err });
66
- throw err;
67
- }
68
- }
69
- getStatus() {
70
- return this.connection.getStatus();
71
- }
72
- ackMsg(channel, message, allUpTo = false) {
73
- const msg = message ?? this.nullMessage;
74
- return channel.ack(msg, allUpTo);
75
- }
76
- nackMsg(channel, message, nackOptions) {
77
- const { requeue = true, allUpTo = false } = nackOptions;
78
- return channel.nack(message, allUpTo, requeue);
79
- }
80
- async createChannelAndListenQueue(queueName) {
81
- const channel = await this.connection.createChannel(queueName);
82
- const consumerOptions = this.getConsumerOptions(queueName) || {};
83
- const { consumerTag: preferredConsumerTag, prefetchCount = this.defaultPrefetchCount } = consumerOptions;
84
- await channel.prefetch(prefetchCount);
85
- const callback = this.onMessageCallback(queueName, channel, consumerOptions);
86
- channel.on('error', async (err) => {
87
- metrics_1.totalListenerChannelErrorsMetric.increment({ queueName });
88
- this.logger.warn('Recreating listener channel because an error has been occurred', { err, queueName });
89
- await this.handleChannelError(queueName, consumerOptions);
90
- });
91
- try {
92
- const { consumerTag } = await channel.consume(queueName, callback, { consumerTag: preferredConsumerTag });
93
- this.saveConsumerTag(queueName, consumerTag);
94
- this.logger.info(`Start consuming queue [${queueName}] with consumerTag [${consumerTag}]`);
95
- }
96
- catch (err) {
97
- this.logger.error('Failed to consume queue', { err, queueName });
98
- await this.handleChannelError(queueName, consumerOptions);
99
- }
100
- await this.saveChannel(queueName, channel);
101
- }
102
- saveQueueCallback(queueName, callback) {
103
- this.queuesCallbacksMap.set(queueName, callback);
104
- }
105
- getQueueCallback(queueName) {
106
- const callback = this.queuesCallbacksMap.get(queueName);
107
- if (!callback) {
108
- const errMsg = `Failed to find callback by queue name [${queueName}]`;
109
- this.logger.error(errMsg);
110
- throw new Error('Error');
111
- }
112
- return callback;
113
- }
114
- async saveChannel(queueName, channel) {
115
- try {
116
- const oldChannel = this.getQueueChannel(queueName);
117
- await oldChannel?.close();
118
- this.queuesChannelsMap.set(queueName, channel);
119
- }
120
- catch (err) {
121
- this.logger.error('Failed to close prev channel', { err, queueName });
122
- }
123
- }
124
- onMessageCallback(queueName, channel, consumerOptions) {
125
- const callback = this.getQueueCallback(queueName);
126
- return async (message) => {
127
- const startTime = process.hrtime.bigint();
128
- if (message === null) {
129
- this.rabbitMQMetrics.collectResponseTotalMetric(startTime, interfaces_1.LabelUnknown, interfaces_1.LabelUnknown, this.systemServiceName, errors_1.ErrorType.Unoperated);
130
- await this.handleChannelError(queueName, consumerOptions);
131
- return;
132
- }
133
- this.queueRecreateChannelTriesMap.set(queueName, 0);
134
- const [messageData, err] = this.parseMessage(message);
135
- const source = message?.properties.headers?.[rabbitmq_1.Headers.sentFrom] || interfaces_1.LabelUnknown;
136
- const eventName = messageData?.event || interfaces_1.LabelUnknown;
137
- this.rabbitMQMetrics.collectCommunicationsTotalMetric(eventName, source, this.systemServiceName, 'inbound', queueName);
138
- if (err || !messageData) {
139
- this.rabbitMQMetrics.collectResponseTotalMetric(startTime, eventName, source, this.systemServiceName, errors_1.ErrorType.Unoperated);
140
- return this.ackMsg(channel, message);
141
- }
142
- const response = this.prepareResponse(message, messageData, channel);
143
- try {
144
- const result = await callback(response);
145
- this.rabbitMQMetrics.collectResponseTotalMetric(startTime, eventName, source, this.systemServiceName);
146
- return result;
147
- }
148
- catch (err) {
149
- const errorType = err instanceof errors_1.ApiError ? err.getType() : errors_1.ErrorType.Unoperated;
150
- this.rabbitMQMetrics.collectResponseTotalMetric(startTime, eventName, source, this.systemServiceName, errorType);
151
- throw err;
152
- }
153
- };
154
- }
155
- async handleChannelError(queueName, consumerOptions) {
156
- const { recreateChannelOptions: { timeout = this.defaultRecreateChannelOptions.timeout, maxTries = this.defaultRecreateChannelOptions.maxTries, backoffCoefficient = this.defaultRecreateChannelOptions.backoffCoefficient, } = {}, } = consumerOptions;
157
- const recreateChannelTriesCounter = (this.queueRecreateChannelTriesMap.get(queueName) || 0) + 1;
158
- this.queueRecreateChannelTriesMap.set(queueName, recreateChannelTriesCounter);
159
- if (maxTries !== this.infiniteRecreateChannelTriesCount && recreateChannelTriesCounter >= maxTries) {
160
- throw new Error(`Max recreate channel tries reached [${recreateChannelTriesCounter}] for queue [${queueName}]`);
161
- }
162
- const waitingTime = timeout * backoffCoefficient * recreateChannelTriesCounter;
163
- await new Promise((resolve) => setTimeout(resolve, waitingTime));
164
- await this.createChannelAndListenQueue(queueName);
165
- }
166
- parseMessage(message) {
167
- try {
168
- const content = message?.content.toString() || '';
169
- return [JSON.parse(content), null];
170
- }
171
- catch (err) {
172
- this.logger.error('Error while parse message content', message);
173
- return [null, err];
174
- }
175
- }
176
- prepareResponse(message, messageData, channel) {
177
- const done = (data) => this.finishResponseProcessing(message, channel, data);
178
- return {
179
- done,
180
- id: message?.properties.messageId,
181
- data: messageData,
182
- properties: message?.properties,
183
- reject: (nackOptions = this.defaultNackOptions) => {
184
- this.nackMsg(channel, message, nackOptions);
185
- },
186
- };
187
- }
188
- finishResponseProcessing(message, channel, data) {
189
- const { replyTo, correlationId } = message.properties;
190
- if (data && replyTo && correlationId) {
191
- const json = JSON.stringify(data);
192
- const content = Buffer.from(json);
193
- const options = {
194
- correlationId,
195
- headers: {
196
- [rabbitmq_1.Headers.handledBy]: this.systemServiceName,
197
- },
198
- };
199
- const exchangeName = '';
200
- const routingKey = replyTo;
201
- channel.publish(exchangeName, routingKey, content, options);
202
- }
203
- else if (!data && replyTo && correlationId) {
204
- this.logger.warn('Reply to and correlationId headers found but no reply data specified');
205
- }
206
- this.ackMsg(channel, message);
207
- }
208
- getConsumerOptions(queueName) {
209
- const consumerOptions = this.queueConsumerOptionsMap.get(queueName);
210
- if (!consumerOptions) {
211
- this.logger.error(`Not found queue [${queueName}] consumer options`);
212
- return;
213
- }
214
- return consumerOptions;
215
- }
216
- getQueueChannel(queueName) {
217
- return this.queuesChannelsMap.get(queueName);
218
- }
219
- saveConsumerTag(queueName, consumerTag) {
220
- const tags = this.queueConsumerTagsMap.get(queueName) || [];
221
- this.queueConsumerTagsMap.set(queueName, [...tags, consumerTag]);
222
- }
223
- }
224
- exports.AmqpListener = AmqpListener;
225
- //# sourceMappingURL=amqpListener.js.map
1
+ import { Headers, NackOptions } from "../../interfaces/providers/rabbitmq/index.js";
2
+ import { LabelUnknown } from "../../interfaces/metrics/index.js";
3
+ import "../../interfaces/index.js";
4
+ import { totalListenerChannelErrorsMetric } from "../../metrics/index.js";
5
+ import { ApiError, ErrorType } from "@diia-inhouse/errors";
6
+ import { setTimeout } from "node:timers/promises";
7
+ //#region src/providers/rabbitmq/amqpListener.ts
8
+ var AmqpListener = class {
9
+ connection;
10
+ logger;
11
+ rabbitMQMetrics;
12
+ queuesOptions;
13
+ systemServiceName;
14
+ infiniteRecreateChannelTriesCount = 0;
15
+ defaultRecreateChannelOptions = {
16
+ maxTries: 6,
17
+ timeout: 1e3,
18
+ backoffCoefficient: 2
19
+ };
20
+ defaultPrefetchCount = 1;
21
+ queuesChannelsMap = /* @__PURE__ */ new Map();
22
+ queueRecreateChannelTriesMap = /* @__PURE__ */ new Map();
23
+ defaultNackOptions = new NackOptions(true, false);
24
+ queuesCallbacksMap = /* @__PURE__ */ new Map();
25
+ queueConsumerOptionsMap = /* @__PURE__ */ new Map();
26
+ queueConsumerTagsMap = /* @__PURE__ */ new Map();
27
+ nullMessage = { fields: { deliveryTag: "unknown" } };
28
+ constructor(connection, logger, rabbitMQMetrics, queuesOptions = [], systemServiceName) {
29
+ this.connection = connection;
30
+ this.logger = logger;
31
+ this.rabbitMQMetrics = rabbitMQMetrics;
32
+ this.queuesOptions = queuesOptions;
33
+ this.systemServiceName = systemServiceName;
34
+ for (const queueOptions of this.queuesOptions) {
35
+ const { name: queueName, consumerOptions: { prefetchCount, ...consumerOpts } = {} } = queueOptions;
36
+ this.queueRecreateChannelTriesMap.set(queueName, 0);
37
+ this.queueConsumerOptionsMap.set(queueName, {
38
+ ...consumerOpts,
39
+ prefetchCount: prefetchCount ?? this.defaultPrefetchCount
40
+ });
41
+ }
42
+ }
43
+ async init() {
44
+ this.connection.on("ready", async () => {
45
+ const tasks = Array.from(this.queuesChannelsMap.keys()).map(async (queueName) => {
46
+ await this.createChannelAndListenQueue(queueName);
47
+ });
48
+ await Promise.all(tasks);
49
+ });
50
+ }
51
+ async cancelQueue(queueName) {
52
+ const channel = this.getQueueChannel(queueName);
53
+ const consumerTags = this.queueConsumerTagsMap.get(queueName) || [];
54
+ for (const consumerTag of consumerTags) await channel?.cancel(consumerTag);
55
+ }
56
+ async listenQueue(queueName, callback) {
57
+ this.logger.debug(`Start listen queue [${queueName}]`);
58
+ try {
59
+ this.saveQueueCallback(queueName, callback);
60
+ await this.createChannelAndListenQueue(queueName);
61
+ } catch (err) {
62
+ this.logger.error(`Error while start listen queue [${queueName}]`, { err });
63
+ throw err;
64
+ }
65
+ }
66
+ getStatus() {
67
+ return this.connection.getStatus();
68
+ }
69
+ ackMsg(channel, message, allUpTo = false) {
70
+ const msg = message ?? this.nullMessage;
71
+ return channel.ack(msg, allUpTo);
72
+ }
73
+ nackMsg(channel, message, nackOptions) {
74
+ const { requeue = true, allUpTo = false } = nackOptions;
75
+ return channel.nack(message, allUpTo, requeue);
76
+ }
77
+ async createChannelAndListenQueue(queueName) {
78
+ const channel = await this.connection.createChannel(queueName);
79
+ const consumerOptions = this.getConsumerOptions(queueName) || {};
80
+ const { consumerTag: preferredConsumerTag, prefetchCount = this.defaultPrefetchCount } = consumerOptions;
81
+ await channel.prefetch(prefetchCount);
82
+ const callback = this.onMessageCallback(queueName, channel, consumerOptions);
83
+ channel.on("error", async (err) => {
84
+ totalListenerChannelErrorsMetric.increment({ queueName });
85
+ this.logger.warn("Recreating listener channel because an error has been occurred", {
86
+ err,
87
+ queueName
88
+ });
89
+ await this.handleChannelError(queueName, consumerOptions);
90
+ });
91
+ try {
92
+ const { consumerTag } = await channel.consume(queueName, callback, { consumerTag: preferredConsumerTag });
93
+ this.saveConsumerTag(queueName, consumerTag);
94
+ this.logger.info(`Start consuming queue [${queueName}] with consumerTag [${consumerTag}]`);
95
+ } catch (err) {
96
+ this.logger.error("Failed to consume queue", {
97
+ err,
98
+ queueName
99
+ });
100
+ await this.handleChannelError(queueName, consumerOptions);
101
+ }
102
+ await this.saveChannel(queueName, channel);
103
+ }
104
+ saveQueueCallback(queueName, callback) {
105
+ this.queuesCallbacksMap.set(queueName, callback);
106
+ }
107
+ getQueueCallback(queueName) {
108
+ const callback = this.queuesCallbacksMap.get(queueName);
109
+ if (!callback) {
110
+ const errMsg = `Failed to find callback by queue name [${queueName}]`;
111
+ this.logger.error(errMsg);
112
+ throw new Error("Error");
113
+ }
114
+ return callback;
115
+ }
116
+ async saveChannel(queueName, channel) {
117
+ try {
118
+ await this.getQueueChannel(queueName)?.close();
119
+ this.queuesChannelsMap.set(queueName, channel);
120
+ } catch (err) {
121
+ this.logger.error("Failed to close prev channel", {
122
+ err,
123
+ queueName
124
+ });
125
+ }
126
+ }
127
+ onMessageCallback(queueName, channel, consumerOptions) {
128
+ const callback = this.getQueueCallback(queueName);
129
+ return async (message) => {
130
+ const startTime = process.hrtime.bigint();
131
+ if (message === null) {
132
+ this.rabbitMQMetrics.collectResponseTotalMetric(startTime, LabelUnknown, LabelUnknown, this.systemServiceName, ErrorType.Unoperated);
133
+ await this.handleChannelError(queueName, consumerOptions);
134
+ return;
135
+ }
136
+ this.queueRecreateChannelTriesMap.set(queueName, 0);
137
+ const [messageData, err] = this.parseMessage(message);
138
+ const source = message?.properties.headers?.[Headers.sentFrom] || "unknown";
139
+ const eventName = messageData?.event || "unknown";
140
+ this.rabbitMQMetrics.collectCommunicationsTotalMetric(eventName, source, this.systemServiceName, "inbound", queueName);
141
+ if (err || !messageData) {
142
+ this.rabbitMQMetrics.collectResponseTotalMetric(startTime, eventName, source, this.systemServiceName, ErrorType.Unoperated);
143
+ return this.ackMsg(channel, message);
144
+ }
145
+ const response = this.prepareResponse(message, messageData, channel);
146
+ try {
147
+ const result = await callback(response);
148
+ this.rabbitMQMetrics.collectResponseTotalMetric(startTime, eventName, source, this.systemServiceName);
149
+ return result;
150
+ } catch (callbackErr) {
151
+ const errorType = callbackErr instanceof ApiError ? callbackErr.getType() : ErrorType.Unoperated;
152
+ this.rabbitMQMetrics.collectResponseTotalMetric(startTime, eventName, source, this.systemServiceName, errorType);
153
+ throw callbackErr;
154
+ }
155
+ };
156
+ }
157
+ async handleChannelError(queueName, consumerOptions) {
158
+ const { recreateChannelOptions: { timeout = this.defaultRecreateChannelOptions.timeout, maxTries = this.defaultRecreateChannelOptions.maxTries, backoffCoefficient = this.defaultRecreateChannelOptions.backoffCoefficient } = {} } = consumerOptions;
159
+ const recreateChannelTriesCounter = (this.queueRecreateChannelTriesMap.get(queueName) || 0) + 1;
160
+ this.queueRecreateChannelTriesMap.set(queueName, recreateChannelTriesCounter);
161
+ if (maxTries !== this.infiniteRecreateChannelTriesCount && recreateChannelTriesCounter >= maxTries) throw new Error(`Max recreate channel tries reached [${recreateChannelTriesCounter}] for queue [${queueName}]`);
162
+ await setTimeout(timeout * backoffCoefficient * recreateChannelTriesCounter);
163
+ await this.createChannelAndListenQueue(queueName);
164
+ }
165
+ parseMessage(message) {
166
+ try {
167
+ const content = message?.content.toString() || "";
168
+ return [JSON.parse(content), null];
169
+ } catch (err) {
170
+ this.logger.error("Error while parse message content", message);
171
+ return [null, err];
172
+ }
173
+ }
174
+ prepareResponse(message, messageData, channel) {
175
+ const done = (data) => this.finishResponseProcessing(message, channel, data);
176
+ return {
177
+ done,
178
+ id: message?.properties.messageId,
179
+ data: messageData,
180
+ properties: message?.properties,
181
+ reject: (nackOptions = this.defaultNackOptions) => {
182
+ this.nackMsg(channel, message, nackOptions);
183
+ }
184
+ };
185
+ }
186
+ finishResponseProcessing(message, channel, data) {
187
+ const { replyTo, correlationId } = message.properties;
188
+ if (data && replyTo && correlationId) {
189
+ const json = JSON.stringify(data);
190
+ const content = Buffer.from(json);
191
+ const options = {
192
+ correlationId,
193
+ headers: { [Headers.handledBy]: this.systemServiceName }
194
+ };
195
+ const exchangeName = "";
196
+ const routingKey = replyTo;
197
+ channel.publish(exchangeName, routingKey, content, options);
198
+ } else if (!data && replyTo && correlationId) this.logger.warn("Reply to and correlationId headers found but no reply data specified");
199
+ this.ackMsg(channel, message);
200
+ }
201
+ getConsumerOptions(queueName) {
202
+ const consumerOptions = this.queueConsumerOptionsMap.get(queueName);
203
+ if (!consumerOptions) {
204
+ this.logger.error(`Not found queue [${queueName}] consumer options`);
205
+ return;
206
+ }
207
+ return consumerOptions;
208
+ }
209
+ getQueueChannel(queueName) {
210
+ return this.queuesChannelsMap.get(queueName);
211
+ }
212
+ saveConsumerTag(queueName, consumerTag) {
213
+ const tags = this.queueConsumerTagsMap.get(queueName) || [];
214
+ this.queueConsumerTagsMap.set(queueName, [...tags, consumerTag]);
215
+ }
216
+ };
217
+ //#endregion
218
+ export { AmqpListener };
@@ -0,0 +1,38 @@
1
+ import { ExchangeName } from "../../interfaces/messageBrokerServiceConfig.js";
2
+ import { ConnectionStatus } from "../../interfaces/providers/rabbitmq/amqpConnection.js";
3
+ import { AmqpConnection } from "./amqpConnection.js";
4
+ import { PublisherOptions } from "../../interfaces/options.js";
5
+ import { QueueMessageData } from "../../interfaces/providers/rabbitmq/index.js";
6
+ import { MessageHeaders, PublishingResult } from "../../interfaces/providers/rabbitmq/amqpPublisher.js";
7
+ import { RabbitMQMetricsService } from "../../services/metrics.js";
8
+ import { Logger } from "@diia-inhouse/types";
9
+
10
+ //#region src/providers/rabbitmq/amqpPublisher.d.ts
11
+ declare class AmqpPublisher {
12
+ private readonly connection;
13
+ private readonly logger;
14
+ private readonly rabbitMQMetrics;
15
+ private readonly systemServiceName;
16
+ private readonly defaultPublishOptions;
17
+ private rpcChannel?;
18
+ private regularChannel?;
19
+ private readonly replyToQueueName;
20
+ private eventEmitter?;
21
+ private readonly directResponseTimeout;
22
+ constructor(connection: AmqpConnection, logger: Logger, rabbitMQMetrics: RabbitMQMetricsService, systemServiceName: string, options?: PublisherOptions);
23
+ init(): Promise<void>;
24
+ publishToExchange(exchangeName: ExchangeName, message: QueueMessageData, headers: MessageHeaders, routingKey?: string): Promise<PublishingResult>;
25
+ publishToExchangeDirect<T>(exchangeName: ExchangeName, message: QueueMessageData, headers: MessageHeaders, routingKey?: string, responseTimeoutMs?: number): Promise<T>;
26
+ getStatus(): ConnectionStatus;
27
+ private createRegularChannel;
28
+ private createRpcChannel;
29
+ private listenReplyToQueue;
30
+ private publishMessage;
31
+ private publishRequest;
32
+ private publish;
33
+ private getPublishOptions;
34
+ private receiveDirectResponse;
35
+ private collectMetrics;
36
+ }
37
+ //#endregion
38
+ export { AmqpPublisher };