@platform-x/hep-message-broker-client 1.1.24 → 1.1.26

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 (48) hide show
  1. package/dist/src/Util/commonUtil.js.map +1 -0
  2. package/dist/src/Util/constants.js.map +1 -0
  3. package/dist/src/Util/logger.js.map +1 -0
  4. package/dist/src/Util/requestTracer.js.map +1 -0
  5. package/dist/src/config/ConfigManager.js.map +1 -0
  6. package/dist/src/config/index.js.map +1 -0
  7. package/dist/src/index.js.map +1 -0
  8. package/dist/src/messageBroker/BaseRabbitMQClient.js.map +1 -0
  9. package/dist/src/messageBroker/ConnectionManager.js.map +1 -0
  10. package/dist/src/messageBroker/MessageBrokerClient.js.map +1 -0
  11. package/dist/src/messageBroker/MessageConsumer.js.map +1 -0
  12. package/dist/src/messageBroker/MessageProducer.js.map +1 -0
  13. package/dist/src/messageBroker/RabbitMQClient.js.map +1 -0
  14. package/dist/src/messageBroker/RetryManager.js.map +1 -0
  15. package/dist/src/messageBroker/interface/ConnectionWrapper.js.map +1 -0
  16. package/dist/src/messageBroker/interface/IMessageBrokerClient.js.map +1 -0
  17. package/dist/src/messageBroker/rabbitmq/MessageBroker.js.map +1 -0
  18. package/dist/src/messageBroker/rabbitmq/MessageBrokerClient.js +6 -3
  19. package/dist/src/messageBroker/rabbitmq/MessageBrokerClient.js.map +1 -0
  20. package/dist/src/messageBroker/types/ActionType.js.map +1 -0
  21. package/dist/src/messageBroker/types/PublishMessageInputType.js.map +1 -0
  22. package/dist/src/models/MessageModel.js.map +1 -0
  23. package/dist/src/models/NotificationMessageModel.js.map +1 -0
  24. package/package.json +1 -1
  25. package/src/Util/commonUtil.ts +41 -0
  26. package/src/Util/constants.ts +9 -0
  27. package/src/Util/logger.ts +219 -0
  28. package/src/Util/requestTracer.ts +28 -0
  29. package/src/config/ConfigManager.ts +35 -0
  30. package/src/config/index.ts +38 -0
  31. package/src/index.ts +74 -0
  32. package/src/messageBroker/BaseRabbitMQClient.ts +30 -0
  33. package/src/messageBroker/ConnectionManager.ts +182 -0
  34. package/src/messageBroker/MessageBrokerClient.ts +88 -0
  35. package/src/messageBroker/MessageConsumer.ts +85 -0
  36. package/src/messageBroker/MessageProducer.ts +142 -0
  37. package/src/messageBroker/RabbitMQClient.ts +47 -0
  38. package/src/messageBroker/RetryManager.ts +64 -0
  39. package/src/messageBroker/interface/ConnectionWrapper.ts +7 -0
  40. package/src/messageBroker/interface/IMessageBrokerClient.ts +11 -0
  41. package/src/messageBroker/rabbitmq/MessageBroker.ts +547 -0
  42. package/src/messageBroker/rabbitmq/MessageBrokerClient.ts +685 -0
  43. package/src/messageBroker/types/ActionType.ts +1 -0
  44. package/src/messageBroker/types/PublishMessageInputType.ts +8 -0
  45. package/src/models/MessageModel.ts +14 -0
  46. package/src/models/NotificationMessageModel.ts +0 -0
  47. package/tsconfig.json +73 -0
  48. package/rabbitMQConfig.json +0 -344
@@ -0,0 +1,685 @@
1
+ // Import the amqplib library for RabbitMQ
2
+ import * as amqp from 'amqplib';
3
+ import config from '../../config';
4
+ import { Logger } from '../../Util/logger';
5
+ import ConnectionWrapper from '../interface/ConnectionWrapper';
6
+ import ConfigManager from '../../config/ConfigManager';
7
+ import { PublishMessageInputType } from '../types/PublishMessageInputType';
8
+ import MessageModel from '../../models/MessageModel';
9
+ import { ActionType } from '../types/ActionType';
10
+ import { SECRET_KEYS } from '../../Util/constants';
11
+ import { getIAMSecrets } from '../..';
12
+ // Remove lodash delay import
13
+ let configManager: ConfigManager = ConfigManager.getInstance();
14
+ // Declare variables for the connection and channel
15
+ let connection: amqp.Connection;
16
+ let channel: amqp.Channel;
17
+ // Declare variable for check connection
18
+ let isConnectionOpen = false;
19
+
20
+ let configData: any = {};
21
+ const RABBITMQ = config?.RABBITMQ;
22
+ let maxRetries: number = RABBITMQ?.MAX_RETRIES;
23
+ let retry_delay: any = RABBITMQ?.TTL?.split("|");
24
+ // let dlx_exchange:string = RABBITMQ.DLX_EXCHANGE;
25
+ let dlx_queue:string = RABBITMQ.DLX_QUEUE;
26
+ // let retry_exchange:string = RABBITMQ.RETRY_EXCHANGE;
27
+ // let retry_queue:string = RABBITMQ.RETRY_QUEUE;
28
+ let connectionRetry:number = 1;
29
+ let retryCountHeader = 'x-retry-count';
30
+ export class MessageBrokerClient {
31
+
32
+ // private readonly correlationId: string;
33
+ // constructor() {
34
+ // this.correlationId = generateCorrelationId();
35
+ // }
36
+
37
+ /**
38
+ * Function to check the status of the connection
39
+ * @returns boolean
40
+ */
41
+ public async isConnected(): Promise<boolean> {
42
+ return isConnectionOpen;
43
+ };
44
+
45
+
46
+ /**
47
+ * Function to initialize the connection, channel and setup the queues and exchanges
48
+ * @param reqData
49
+ * @returns
50
+ */
51
+ public async initialize() {
52
+ Logger.info('Reached to initialize', 'initialize');
53
+ configData = configManager.getConfig();
54
+ // dlx_exchange = configData?.dlx_exchange;
55
+ dlx_queue = configData?.dlx_queue;
56
+ // retry_exchange = configData?.retry_exchange;
57
+ // retry_queue = configData?.retry_queue;
58
+ let retries: number = 0;
59
+ // Retrie times connection
60
+ while (retries < maxRetries) {
61
+ try {
62
+ if(RABBITMQ.CONNECTION_ERROR === 'true' || RABBITMQ.CONNECTION_ERROR === true){
63
+ throw new Error("Error in connection");
64
+ }
65
+ await this.createConnection();
66
+ return { connection: connection, channel: channel };
67
+ } catch (err) {
68
+ retries++;
69
+ Logger.error('initialize:Failed to connect to RabbitMQ', `Attempt ${retries} of ${maxRetries}.`);
70
+ if (retries >= maxRetries) {
71
+ Logger.error('Max retries reached, cannot connect to RabbitMQ.', 'initialize');
72
+ return { connection, channel };
73
+ }
74
+ // Wait before retrying (e.g., 10 seconds)
75
+ await new Promise(resolve => setTimeout(resolve, 10000));
76
+ }
77
+ }
78
+ };
79
+
80
+
81
+
82
+ /**
83
+ * Function to publish a message with retry
84
+ * @param request
85
+ * @param classInstance
86
+ * @param messagePublisherErrorHandler
87
+ * @returns
88
+ */
89
+ public async publishMessage(
90
+ request: PublishMessageInputType
91
+ // , classInstance:any, messagePublisherErrorHandler:string
92
+ ){
93
+ Logger.info('Reached:publishMessage', 'publishMessage');
94
+ // Logger.debug('Reached:publishMessageToExchange', 'publishMessageToExchange',{classInstance, messagePublisherErrorHandler});
95
+ try{
96
+ if(isConnectionOpen){
97
+ Logger.info('Check connection open or not', 'publishMessage');
98
+ const queueName: string = request.queueName;
99
+ const data: MessageModel = request.message;
100
+ const messageBuffer = Buffer.from(JSON.stringify(data?.message));
101
+ // let attempt: number = 0;
102
+ // let retries: number = typeof data.message === 'object' && data.message !== null ? (data.message as { retries: number }).retries ?? 0 : 0;
103
+ // while (attempt < maxRetries ) {
104
+ Logger.info('Max retries check', 'publishMessage');
105
+ const isSent = await new Promise<boolean>((resolve, reject) => {
106
+ let sent = channel.sendToQueue(queueName, Buffer.from(messageBuffer), {
107
+ persistent: true, // Ensures the message is persisted
108
+ // correlationId: this.correlationId,
109
+ });
110
+ if (sent) {
111
+ Logger.debug(`Message sent successfully`, 'publishMessage', { data, queueName });
112
+ resolve(true);
113
+ } else {
114
+ Logger.error(`Failed to publish message.`, 'publishMessage', { message: messageBuffer.toString() });
115
+ reject(false);
116
+ }
117
+ });
118
+ if((isSent) && (RABBITMQ.PUBLISHER_ERROR !== 'true' && RABBITMQ.PUBLISHER_ERROR !== true)){
119
+ return isSent;
120
+ }
121
+ // else{
122
+ // let queueName = (request as PublishMessageInputType)?.queueName
123
+ // if (attempt >= maxRetries) {
124
+ // Logger.error('Max retries reached. Failed to publish message.', 'publishMessage', request);
125
+ // // Optionally: Log or persist the failed message to a DB, file, or other storage for manual intervention
126
+ // this.sendMessageToDeadLatter(attempt, data, queueName);
127
+ // this.messagePublisherErrorHandler(request); // we need to comment this line
128
+ // if (typeof classInstance[messagePublisherErrorHandler] === 'function') {
129
+ // classInstance[messagePublisherErrorHandler](data); // Dynamically call the method
130
+ // } else {
131
+ // Logger.info(`Method ${messagePublisherErrorHandler} not found in classInstance.`, 'publishMessage');
132
+ // }
133
+ // return false;
134
+ // }else{
135
+ // await this.publishMessageRetryQueue(request, attempt);
136
+ // attempt++;
137
+ // }
138
+ // }
139
+ // }
140
+
141
+ }
142
+ } catch (error) {
143
+ Logger.error(`Error sending message: ${error}`, 'sendMessage');
144
+ throw new Error("Failed to publish message to queue");
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Function to publish a message with retry
150
+ * @param request
151
+ * @param classInstance
152
+ * @param messagePublisherErrorHandler
153
+ * @returns
154
+ */
155
+ public async publishMessageToExchange(request: PublishMessageInputType): Promise<boolean> {
156
+ Logger.info('Reached:publishMessageToExchange', 'publishMessageToExchange');
157
+ let attempt = 0;
158
+ const exchangeName: string | undefined = request?.exchangeName ?? '';
159
+ const queueName: string = request.queueName;
160
+ const data: MessageModel = request?.message;
161
+ const messageBuffer = Buffer.from(JSON.stringify(data.message));
162
+ try {
163
+ // 1. Ensure connection and channel exists
164
+ await this.checkConnectionAndChannel();
165
+
166
+ // 2. Retry loop
167
+ while (attempt < maxRetries) {
168
+ try {
169
+ Logger.info(`Attempting to publish message`, 'publishMessageToExchange');
170
+ let sent = channel.publish(exchangeName, queueName, messageBuffer, {
171
+ persistent: true,
172
+ });
173
+ // if(attempt <1){ sent = false}else{sent = true} // Simulate failure for testing retry logic
174
+ if (sent && (RABBITMQ.PUBLISHER_ERROR !== 'true' && RABBITMQ.PUBLISHER_ERROR !== true)) {
175
+ Logger.info(`Message published successfully on attempt ${attempt + 1}`, 'publishMessageToExchange');
176
+ Logger.info(`✅ Message sent successfully`, 'publishMessageToExchange');
177
+ break;
178
+ } else {
179
+ throw new Error('Publish returned false');
180
+ }
181
+ } catch (error) {
182
+ attempt++;
183
+ Logger.error(`Failed attempt ${attempt} to publish message`, 'publishMessageToExchange', error);
184
+ if (attempt < maxRetries) {
185
+ Logger.info(`Retrying in ${retry_delay}ms...`, 'publishMessageToExchange');
186
+ await new Promise(res => setTimeout(res, retry_delay));
187
+ }else {
188
+ Logger.error(`Error in publishMessageToExchange: ${error}`, 'publishMessageToExchange');
189
+ throw new Error('Max retries reached');
190
+ }
191
+ }
192
+ }
193
+ return true;
194
+ } catch (error) {
195
+ Logger.error('Max retries reached. Sending message to Dead Letter Queue...', 'publishMessageToExchange');
196
+ await this.sendMessageToDeadLatter(attempt, data);
197
+ const customError = new Error('Failed to publish message to exchange');
198
+ (customError as any).originalError = error; // optional
199
+ throw customError;
200
+ }
201
+ }
202
+ /**
203
+ * Funciton to message handler
204
+ */
205
+ // TO Do rename function name messagePublisherErrorHandler
206
+ public async messagePublisherErrorHandler(request: PublishMessageInputType): Promise<boolean> {
207
+ Logger.info(`${JSON.stringify(request)}`, 'messagePublisherErrorHandler');
208
+ return Promise.resolve(true);
209
+ }
210
+
211
+ /**
212
+ * Funvction for initialize consumer queue
213
+ * @param consumerQueues
214
+ */
215
+ public async initializeConsumers(classInstance:any, consumerHandler:string, consumerErrorHandler:string,serviceConsumerQueues?: {}) {
216
+ try {
217
+ Logger.info('Reached in initialize consumer', `initializeConsumers`);
218
+ Logger.debug('Consumer queues serviceConsumerQueues : ', 'initializeConsumers', { serviceConsumerQueues });
219
+
220
+ let consume_queues = serviceConsumerQueues ?? configData?.consume_queues;
221
+ // Consume messages from all queues
222
+ for (let queueName in consume_queues) {
223
+ if (Object.prototype.hasOwnProperty.call(consume_queues, queueName)) {
224
+ // Call function Consume messages with retry
225
+ await this.consumeMessage(queueName, classInstance, consumerHandler, consumerErrorHandler,);
226
+ }
227
+ }
228
+ } catch (error: unknown) {
229
+ Logger.error('Error initializing consumers:', 'initializeConsumers', (error as Error).message);
230
+ throw new Error((error as Error).message);
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Function for perform action with retry
236
+ * @param actionHandler
237
+ * @param context
238
+ * @param failActionHandler
239
+ * @returns
240
+ */
241
+ public async performActionWithRetry<T>(actionHandler: ActionType<T>, failActionHandler: ActionType<T>, context: T): Promise<boolean> {
242
+ Logger.info("Reached: in perform action with retry ", "performActionWithRetry")
243
+ let attempt: number = 0;
244
+ while (attempt < maxRetries) {
245
+ try {
246
+ actionHandler(context);
247
+ return true;
248
+ } catch (error) {
249
+ attempt++;
250
+ Logger.error(`Failed to publish message. Attempt ${attempt} of ${maxRetries}. Error:`, 'performActionWithRetry', { message: (error as Error).message });
251
+ let queueName = (context as PublishMessageInputType)?.queueName
252
+ const data: MessageModel = (context as PublishMessageInputType)?.message;
253
+ if (attempt >= maxRetries) {
254
+ Logger.error('Max retries reached. Failed to publish message.', 'performActionWithRetry');
255
+ // Optionally: Log or persist the failed message to a DB, file, or other storage for manual intervention
256
+ this.sendMessageToDeadLatter(attempt, data);
257
+ failActionHandler(context);
258
+ return false;
259
+ }
260
+ // Wait before retrying (Exponential Backoff)
261
+ let retryDelay: number;
262
+ retryDelay = Number(retry_delay[attempt- 1]);
263
+ let count:any = retry_delay.length - 1 === attempt?retry_delay.length - 1:retry_delay[attempt - 1];
264
+ retryDelay = Number(count);
265
+ this.sendMessageToRetryQueue(attempt, data, retryDelay, queueName);
266
+ Logger.info(`Retrying in ${retryDelay / 1000} seconds...`, 'performActionWithRetry');
267
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
268
+ }
269
+ }
270
+ return false;
271
+ }
272
+
273
+
274
+
275
+ /**
276
+ * Function to consume message with retry
277
+ * @param queueName
278
+ */
279
+ public async consumeMessage(queueName: string, classInstance: any, consumerHandler: string , consumerErrorHandler:string) {
280
+ Logger.info("Reached: Consuming messages from the queue", 'consumeMessage')
281
+ let attempt:number = 0;
282
+ if(isConnectionOpen){
283
+ return await channel.consume(queueName, async (message: any) => {
284
+ if(message){
285
+ Logger.info(`Message received from queue ${queueName}: ${JSON.stringify(message)}`, 'consumeMessage');
286
+ let msgData: any = JSON.parse(message.content.toString());
287
+ Logger.info(`Received message from queue ${queueName}: ${JSON.stringify(msgData)}`, 'consumeMessage');
288
+ const headers = message.properties.headers || {};
289
+ attempt = headers[retryCountHeader] || 0;
290
+ const data:any = {body:msgData?.result?.data, queue_name:queueName, language: msgData?.language, correlationId: msgData?.correlationId, retries: msgData?.retries, item_id: msgData?.item_id, metadatakey: msgData?.metadatakey};
291
+ try {
292
+ let success:any = {};
293
+ if(RABBITMQ.CONSUMER_ERROR !== 'true' && RABBITMQ.CONSUMER_ERROR !== true){
294
+ Logger.info("Consumer handler call for process the request", 'consumeMessage')
295
+ // success = await this.consumerHandler({ queueName, message: msgData });
296
+ if (typeof classInstance[consumerHandler] === 'function') {
297
+ success = await classInstance[consumerHandler]({ queueName, message: msgData }); // Dynamically call the method
298
+ } else {
299
+ Logger.info(`Method ${consumerHandler} not found in classInstance.`, 'consumerMessage');
300
+ }
301
+ }
302
+ if(success?.isMatch === true){
303
+ if (!success?.status) {
304
+ const retryCount = message.properties.headers['x-retry-count'] || 0;
305
+ // while (retryCount < maxRetries) {
306
+ attempt = retryCount?retryCount:attempt;
307
+ if (retryCount >= maxRetries) {
308
+ Logger.error('Max retries reached. Failed to publish message.', 'consumeMessage');
309
+ // Optionally: Log or persist the failed message to a DB, file, or other storage for manual intervention
310
+ this.sendMessageToDeadLatter(attempt, data);
311
+ // this.consumerErrorHandler(data);
312
+ if (typeof classInstance[consumerErrorHandler] === 'function') {
313
+ classInstance[consumerErrorHandler](data); // Dynamically call the method
314
+ } else {
315
+ Logger.info(`Method ${consumerErrorHandler} not found in classInstance.`, 'consumerMessage');
316
+ }
317
+ channel.nack(message, false, false); // Move message to DLQ after failure
318
+ return false;
319
+ }
320
+ attempt++;
321
+ if (data?.retries >=0) {
322
+ data.retries = attempt;
323
+ }
324
+ // Wait before retrying (Exponential Backoff)
325
+ let retryDelay: number;
326
+ let count:any = retry_delay.length === attempt?retry_delay[retry_delay.length - 1]:retry_delay[attempt-1];
327
+ retryDelay = Number(count);
328
+ await this.sendMessageToRetryQueue(attempt, data, retryDelay, queueName);
329
+ Logger.info(`Retrying in ${retryDelay / 1000} seconds...`, 'consumeMessage');
330
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
331
+ } else {
332
+ Logger.info(`Acknowledge the successful processing`, 'consumeMessage');
333
+ }
334
+ // Acknowledge the successful processing
335
+ channel.ack(message);
336
+ }
337
+ } catch (error) {
338
+ let errorMessage:string = (error as Error).message;
339
+ Logger.error(`Error processing message`, 'consumeMessage', { message: errorMessage});
340
+ this.sendMessageToDeadLatter(attempt, data);
341
+ // this.consumerErrorHandler(data);
342
+ if (typeof classInstance[consumerErrorHandler] === 'function') {
343
+ classInstance[consumerErrorHandler](data); // Dynamically call the method
344
+ } else {
345
+ Logger.info(`Method ${consumerErrorHandler} not found in classInstance.`, 'consumerMessage');
346
+ }
347
+ channel.nack(message, false, false); // Move message to DLQ after failure
348
+ }
349
+ }
350
+ });
351
+ }
352
+ };
353
+
354
+ // /**
355
+ // * Function for consumer massage handler
356
+ // * @param request
357
+ // * @returns
358
+ // */
359
+ // public async consumerHandler(request: PublishMessageInputType): Promise<boolean> {
360
+ // Logger.info(`${JSON.stringify(request)}`, 'consumerHandler');
361
+ // return true;
362
+ // }
363
+
364
+ // /**
365
+ // * Function to consumer message error handler
366
+ // * @param request
367
+ // * @returns
368
+ // */
369
+
370
+ // public async consumerErrorHandler(request:any):Promise<boolean> {
371
+ // Logger.info(`${JSON.stringify(request)}`, 'consumerErrorHandler');
372
+ // return true;
373
+ // }
374
+
375
+
376
+ /**
377
+ * Function to send message to dead later queue
378
+ * @param attempt
379
+ * @param data
380
+ * @param queueName
381
+ * @retruns
382
+ */
383
+
384
+ public async sendMessageToDeadLatter(attempt:number, data:any){
385
+ Logger.info("Reached: send message on dead letter queue", 'sendMessageToDeadLater')
386
+ try {
387
+ if(isConnectionOpen){
388
+ Logger.info(`Attempting to send message to DLX queue: ${dlx_queue} (attempt: ${attempt}, retry-count: ${attempt - 1}, size: ${JSON.stringify(data).length} bytes)`, 'sendMessageToDeadLater');
389
+ const sent = channel.sendToQueue(dlx_queue, Buffer.from(JSON.stringify(data)), {
390
+ persistent: true,
391
+ headers: {
392
+ 'x-retry-count': attempt-1
393
+ },
394
+ });
395
+ if (sent) {
396
+ Logger.info(`Message successfully published to DLX queue: ${dlx_queue} (attempt: ${attempt}, retry-count: ${attempt - 1})`, 'sendMessageToDeadLater');
397
+ } else {
398
+ Logger.error(`Failed to publish message to DLX queue: ${dlx_queue} - Channel buffer full (attempt: ${attempt}, retry-count: ${attempt - 1})`, 'sendMessageToDeadLater');
399
+ }
400
+ return sent;
401
+ } else {
402
+ Logger.error(`Cannot send message to DLX - Connection is not open (queue: ${dlx_queue}, attempt: ${attempt})`, 'sendMessageToDeadLater');
403
+ return false;
404
+ }
405
+ } catch (error:unknown) {
406
+ Logger.error(`Error sending message to dead letter queue: ${(error as Error).message} (queue: ${dlx_queue}, attempt: ${attempt})`, 'sendMessageToDeadLater');
407
+ throw error;
408
+ }
409
+ }
410
+
411
+
412
+ /***
413
+ * Function to send message to retry queue
414
+ * @param attempt
415
+ * @param data
416
+ * @param retryDelay
417
+ * @param queueName
418
+ * @returns
419
+ */
420
+
421
+ public async sendMessageToRetryQueue(attempt:number, data:any, retryDelay:any, queueName:string){
422
+ Logger.info("Reached: send message on retry queue", 'sendMessageToRetryQueue')
423
+ try {
424
+ if(isConnectionOpen){
425
+ const isSent = await new Promise<boolean>((resolve, reject) => {
426
+ const messageBuffer = Buffer.from(JSON.stringify(data));
427
+ let sent = channel.sendToQueue(queueName, messageBuffer, {
428
+ headers: {
429
+ 'x-retry-count': attempt + 1,
430
+ 'x-message-ttl': retryDelay,
431
+ }
432
+ });
433
+ if (sent) {
434
+ Logger.debug(`Message sent successfully`, 'publishMessage', { data, queueName });
435
+ resolve(true);
436
+ } else {
437
+ Logger.error(`Failed to publish message.`, 'publishMessage', { message: data.toString() });
438
+ reject(false);
439
+ }
440
+ });
441
+ return isSent;
442
+ }
443
+ } catch (error:unknown) {
444
+ Logger.error("Error in send message on retry queue", 'sendMessageToRetryQueue', {message: (error as Error ).message});
445
+ throw error;
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Function to find the queue, exchange and add binding
451
+ * @param queueName
452
+ * @param exchangeName
453
+ * @returns
454
+ */
455
+ public async findQueueAndExchange(queueName:string, exchangeName:string){
456
+ Logger.info('Reached to findQueueAndExchange', 'findQueueAndExchange');
457
+ try {
458
+ let exchanges: any = configData?.exchanges;
459
+ let queues: any = configData.queues;
460
+ let bindings: any = configData.bindings;
461
+ // Declare the queues with dlx
462
+
463
+ for (let data of queues) {
464
+ if(data?.name === queueName){
465
+ await this.createQueue(data);
466
+ }
467
+ Logger.info(`${data?.name} queue created `, 'findQueueAndExchange');
468
+ }
469
+
470
+ // Declare the exchange with dlx
471
+ for (let data of exchanges) {
472
+ if(data?.name === exchangeName){
473
+ await this.createExchange(data);
474
+ }
475
+ Logger.info(`${data?.name} exchange created`, 'findQueueAndExchange');
476
+ }
477
+
478
+ // Declare the bindings for queues and exchanges with routing keys
479
+ for (let obj in bindings) {
480
+ bindings[obj]?.forEach(async (key: any) => {
481
+ if(obj === exchangeName && key?.queue === queueName){
482
+ await this.bindQueueAndExchanges(obj, key?.queue, key?.routingKey);
483
+ }
484
+ Logger.info(`${key?.queue} bind with exchange name ${obj} and routing key ${key?.routingKey}`, 'findQueueAndExchange');
485
+ })
486
+ }
487
+ } catch (error) {
488
+ Logger.error("Error on setup queues, exchanges and binding", 'findQueueAndExchange', (error as Error).message);
489
+ throw error;
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Function to check connection and channel
495
+ */
496
+ private async checkConnectionAndChannel() {
497
+ Logger.info('Reached: checkConnectionAndChannel', 'checkConnectionAndChannel');
498
+ // 🧪 1. Ensure connection exists
499
+ if (!isConnectionOpen) {
500
+ Logger.warn('No connection found. Creating new connection...', 'publishMessageToExchange');
501
+ await this.createConnection();
502
+ }
503
+ // 🧪 2. Ensure channel is alive
504
+ const channelConnection: any = channel?.connection;
505
+ if (!channel || !channelConnection || channelConnection.stream.destroyed) {
506
+ Logger.warn('Channel seems dead, recreating before publish...', 'publishMessageToExchange');
507
+ await this.recreateChannel();
508
+ }
509
+ }
510
+
511
+ /**
512
+ * Function to recreate channel
513
+ */
514
+ private async recreateChannel() {
515
+ try {
516
+ channel = await connection.createConfirmChannel();
517
+ channel.prefetch(RABBITMQ?.PREFETCH_COUNT)
518
+ Logger.info(' Channel recreated successfully.', 'recreateChannel');
519
+ } catch (err: any) {
520
+ Logger.error(' Failed to recreate channel:', err);
521
+ }
522
+ }
523
+
524
+ /**
525
+ * Function to setup the queues and exchanges
526
+ * @params array
527
+ * @rtrun
528
+ */
529
+ private async setupQueuesAndExchanges(): Promise<void> {
530
+ Logger.info('Reached to setupQueuesAndExchanges', 'setupQueuesAndExchanges');
531
+ try {
532
+ let exchanges: any = configData.exchanges;
533
+ let queues: any = configData.queues;
534
+ let bindings: any = configData.bindings;
535
+ // Declare the exchange with dlx
536
+ for (let data of exchanges) {
537
+ await this.createExchange(data);
538
+ Logger.info(`${data?.name} exchange created`, 'setupQueuesAndExchanges');
539
+ }
540
+
541
+ // Declare the queues with dlx
542
+ for (let data of queues) {
543
+ await this.createQueue(data);
544
+ Logger.info(`${data?.name} queue created `, 'setupQueuesAndExchanges');
545
+ }
546
+
547
+ // Declare the bindings for queues and exchanges with routing keys
548
+ for (let obj in bindings) {
549
+ bindings[obj]?.forEach(async (key: any) => {
550
+ await this.bindQueueAndExchanges( obj, key?.queue, key?.routingKey);
551
+ Logger.info(`${key?.queue} bind with exchange name ${obj} and routing key ${key?.routingKey}`, 'setupQueuesAndExchanges');
552
+ })
553
+ }
554
+ } catch (error) {
555
+ Logger.error("Error on setup queues, exchanges and binding", 'setupQueuesAndExchanges', (error as Error).message);
556
+ }
557
+ };
558
+
559
+
560
+ /**
561
+ * Function to binding queue and exchange
562
+ * @param exchangeName
563
+ * @param queueName
564
+ * @param routingKey
565
+ */
566
+
567
+ private async bindQueueAndExchanges(exchangeName:string, queueName:string, routingKey:string):Promise<void> {
568
+ try {
569
+ await channel.bindQueue(queueName, exchangeName, routingKey, { 'toQueue':queueName, 'x-match': 'any' });
570
+ } catch (error:unknown) {
571
+ Logger.error('Error in bind Queue and exchange', 'bindQueueAndExchanges', {message:(error as Error).message});
572
+ }
573
+
574
+ }
575
+
576
+
577
+ /**
578
+ * Function to create the queue
579
+ * @param data
580
+ */
581
+
582
+ private async createQueue(data:any ):Promise<void> {
583
+ try {
584
+ await channel.assertQueue(data?.name, { durable: data.durable, exclusive: data.exclusive, autoDelete: data.autoDelete, arguments: data.arguments });
585
+ } catch (error:unknown) {
586
+ Logger.error('Error in Create Queue', 'createQueue', {message:(error as Error).message});
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Function to create the exchange
592
+ * @param data
593
+ */
594
+
595
+ private async createExchange(data:any ):Promise<void> {
596
+ try {
597
+ await channel.assertExchange(data?.name, data.type, { durable: data.durable, autoDelete: data.autoDelete });
598
+ } catch (error:unknown) {
599
+ Logger.error('Error in Create exchange', 'createExchange', {message:(error as Error).message});
600
+ }
601
+ }
602
+
603
+ // private async publishMessageRetryQueue(request: PublishMessageInputType, attempt: number) {
604
+ // try {
605
+ // Logger.error(`Failed to publish message. Attempt ${attempt} of ${maxRetries}. Error:`, 'publishMessageRetryQueue', request);
606
+ // let queueName = (request as PublishMessageInputType)?.queueName
607
+ // const data: MessageModel = (request as PublishMessageInputType)?.message;
608
+ // if (typeof data.message === 'object' && data.message !== null) {
609
+ // (data.message as { retries: number }).retries = attempt;
610
+ // }
611
+ // // Wait before retrying (Exponential Backoff)
612
+ // let retryDelay: number;
613
+ // retryDelay = Number(retry_delay[attempt]);
614
+ // let count:any = retry_delay.length - 1 === attempt?retry_delay[retry_delay.length - 1]:retry_delay[attempt];
615
+ // retryDelay = Number(count);
616
+ // await this.sendMessageToRetryQueue(attempt, data, retryDelay, queueName);
617
+ // Logger.info(`Retrying in ${retryDelay / 1000}, seconds...`, 'publishMessageRetryQueue');
618
+ // await new Promise((resolve) => setTimeout(resolve, retryDelay));
619
+ // } catch (error) {
620
+ // Logger.error(`Failed to publish message. Attempt ${attempt} of ${maxRetries}. Error:`, 'publishMessageRetryQueue', request);
621
+ // throw error;
622
+ // }
623
+ // }
624
+
625
+ /**
626
+ * Function to connect create to RabbitMQ
627
+ */
628
+ public async createConnection(): Promise<ConnectionWrapper> {
629
+ try {
630
+ Logger.info('Reached to createConnection', 'createConnection');
631
+ configManager.loadConfig(RABBITMQ?.CONFIG_PATH);
632
+ const secret = await getIAMSecrets();
633
+ let url: string = `amqp://${secret?.[SECRET_KEYS.RABBITMQ_USER]}:${secret?.[SECRET_KEYS.RABBITMQ_PASS]}@${RABBITMQ?.HOST}`;
634
+ // Create a connection to RabbitMQ server
635
+ connection = await amqp.connect(url, { heartbeat: RABBITMQ?.HEARTBEAT });
636
+ isConnectionOpen = true;
637
+ // check the connection status
638
+ this.bindConnectionEventHandler();
639
+ // create channel
640
+ channel = await connection.createConfirmChannel();
641
+ channel.prefetch(RABBITMQ?.PREFETCH_COUNT);
642
+ Logger.info('RabbitMQ connected successfully! ', 'createConnection');
643
+ // create exchange and queue and bind with exchange
644
+ await this.setupQueuesAndExchanges();
645
+ const connectionWrapper: ConnectionWrapper = { connection, channel }
646
+ return connectionWrapper;
647
+ } catch (error) {
648
+ // Log and rethrow any errors that occur during connection
649
+ Logger.error('Failed to connect to RabbitMQ', 'connection', (error as Error)?.message);
650
+ return { connection: null, channel: null } as unknown as ConnectionWrapper;
651
+ }
652
+ }
653
+
654
+ /**
655
+ * Function for bind connection event handler on close and error setupQueuesAndExchanges
656
+ * @returns
657
+ */
658
+ private bindConnectionEventHandler(): boolean {
659
+ try {
660
+ connection.on('close', async(err: any) => {
661
+ Logger.error(`RabbitMQ: bindConnectionEventHandler:close`, `Connection closed`, err);
662
+ isConnectionOpen = false;
663
+ if(connectionRetry<= maxRetries){
664
+ await this.createConnection();
665
+ connectionRetry++;
666
+ }
667
+ });
668
+ connection.on('error', async(err: any) => {
669
+ Logger.error(`RabbitMQ: bindConnectionEventHandler:error`, `Connection error`, err);
670
+ isConnectionOpen = false;
671
+ if(connectionRetry<= maxRetries){
672
+ await this.createConnection();
673
+ connectionRetry++;
674
+ }
675
+ });
676
+ return isConnectionOpen;
677
+ } catch (error) {
678
+ isConnectionOpen = false;
679
+ return isConnectionOpen;
680
+ }
681
+
682
+ }
683
+
684
+ }
685
+
@@ -0,0 +1 @@
1
+ export type ActionType<T> = (input: T) => void;