@autofleet/rabbit 3.2.23-beta.1.0 → 3.2.24-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ import { EventEmitter } from 'events';
3
3
  import { AmqpConnectionManager, ChannelWrapper, CreateChannelOpts } from 'amqp-connection-manager';
4
4
  import { ConfirmChannel, ConsumeMessage, Options, Replies } from 'amqplib';
5
5
  import { RedisConfig } from './lib/redis';
6
- import { sendCeleryTaskViaHttp } from './lib/celery';
7
- import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary } from './lib/types';
6
+ import { ConnectionPurpose } from './lib/consts';
7
+ import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary, ConnectionData } from './lib/types';
8
8
  export interface IAfRabbitMq {
9
9
  ack: any;
10
10
  nack: any;
@@ -38,10 +38,12 @@ type newChannelOpts = {
38
38
  name?: string;
39
39
  onClose?: null | ((args: any | null) => void);
40
40
  options?: CreateChannelOpts | undefined;
41
+ connectionPurpose?: ConnectionPurpose;
41
42
  };
42
43
  type assertChannelOpts = {
43
44
  channelName?: string;
44
45
  force?: boolean;
46
+ connectionPurpose?: ConnectionPurpose;
45
47
  };
46
48
  declare class RabbitMq implements IAfRabbitMq {
47
49
  static parseMsg(msg: any): any;
@@ -61,9 +63,11 @@ declare class RabbitMq implements IAfRabbitMq {
61
63
  channel: ChannelWrapper | null;
62
64
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
63
65
  blockReconnect: boolean | null | undefined;
64
- connection: AmqpConnectionManager | null | undefined;
66
+ connectionsMap: {
67
+ [ConnectionPurpose.Consume]: ConnectionData;
68
+ [ConnectionPurpose.Publish]: ConnectionData;
69
+ };
65
70
  em: EventEmitter;
66
- creatingConnection: boolean;
67
71
  exchanges: ExchangesCache;
68
72
  queues: QueuesCache;
69
73
  queueSetupPromises: QueueSetupPromisesDictionary;
@@ -78,16 +82,16 @@ declare class RabbitMq implements IAfRabbitMq {
78
82
  private shouldConsumeMessageByTimestamp;
79
83
  ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
80
84
  nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
81
- getConnection(): Promise<AmqpConnectionManager>;
82
- getNewChannel({ name, onClose, options }?: newChannelOpts): Promise<ChannelWrapper>;
83
- assertChannel({ force }?: assertChannelOpts): Promise<ChannelWrapper>;
85
+ getConnection(connectionPurpose: ConnectionPurpose): Promise<AmqpConnectionManager | null | undefined>;
86
+ getNewChannel({ name, onClose, options, connectionPurpose, }: newChannelOpts): Promise<ChannelWrapper>;
87
+ assertChannel({ force, connectionPurpose }: assertChannelOpts): Promise<ChannelWrapper>;
84
88
  assertExchange(exchangeName: string, options?: any): Promise<any>;
85
- getQueueLength(queue: string): Promise<Replies.AssertQueue>;
89
+ getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
86
90
  private deleteQueue;
87
- bindQueue(queue: string, exchange: string): Promise<void>;
88
- setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
91
+ bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose): Promise<void>;
92
+ setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
89
93
  static shouldUseQuorum(queueName: string): boolean;
90
- assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
94
+ assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<any>;
91
95
  private saveConsumer;
92
96
  consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
93
97
  private lockRedisIfNeeded;
@@ -100,4 +104,4 @@ declare class RabbitMq implements IAfRabbitMq {
100
104
  gracefulShutdown(signal: string): Promise<void>;
101
105
  }
102
106
  export default RabbitMq;
103
- export { sendCeleryTaskViaHttp, };
107
+ export { sendCeleryTaskViaHttp, } from './lib/celery';
package/dist/index.js CHANGED
@@ -16,8 +16,6 @@ const logger_1 = __importDefault(require("./logger"));
16
16
  const rabbitError_1 = __importDefault(require("./lib/rabbitError"));
17
17
  const redis_1 = __importDefault(require("./lib/redis"));
18
18
  const utils_1 = require("./lib/utils");
19
- const celery_1 = require("./lib/celery");
20
- Object.defineProperty(exports, "sendCeleryTaskViaHttp", { enumerable: true, get: function () { return celery_1.sendCeleryTaskViaHttp; } });
21
19
  const consts_1 = require("./lib/consts");
22
20
  const types_1 = require("./lib/types");
23
21
  // const debug = nodeDebug('af-rabbitmq')
@@ -121,8 +119,10 @@ class RabbitMq {
121
119
  this.em = new events_1.EventEmitter();
122
120
  this.channel = null;
123
121
  this.publishChannelSetupPromise = null;
124
- this.connection = null;
125
- this.creatingConnection = false;
122
+ this.connectionsMap = {
123
+ [consts_1.ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
124
+ [consts_1.ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
125
+ };
126
126
  this.exchanges = {};
127
127
  this.queues = {};
128
128
  this.queueSetupPromises = {};
@@ -144,30 +144,31 @@ class RabbitMq {
144
144
  });
145
145
  }
146
146
  }
147
- async getConnection() {
147
+ async getConnection(connectionPurpose) {
148
148
  return new Promise(async (resolve, reject) => {
149
+ const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
149
150
  if (this.blockReconnect) {
150
151
  debug('rabbit: block reconnect');
151
152
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
152
153
  // @ts-ignore
153
154
  return resolve();
154
155
  }
155
- if (this.connection !== null) {
156
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
156
+ if (connection !== null) {
157
+ if (this.options?.disableReconnect || connection?.isConnected()) {
157
158
  debug('rabbit: connection - is connected');
158
159
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
159
160
  // @ts-ignore
160
- return resolve(this.connection);
161
+ return resolve(connection);
161
162
  }
162
163
  debug('rabbit: connection - reconnecting');
163
164
  }
164
- if (this.creatingConnection) {
165
+ if (creatingConnection) {
165
166
  debug('rabbit: creating connection emi');
166
167
  this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
167
168
  this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
168
169
  return;
169
170
  }
170
- this.creatingConnection = true;
171
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
171
172
  let isResolved = false;
172
173
  // It is import to use it as a function and not as a variable
173
174
  // because of k8s changes the env variables
@@ -180,11 +181,11 @@ class RabbitMq {
180
181
  return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
181
182
  };
182
183
  const defaultUrls = findServers();
183
- const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
184
+ const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
184
185
  findServers,
185
186
  });
186
- this.connection = connection;
187
- this.connection.on('error', (err) => {
187
+ this.connectionsMap[connectionPurpose].connection = newConnection;
188
+ newConnection.on('error', (err) => {
188
189
  logger_1.default.error('rabbit: connection error', { err });
189
190
  if (!isResolved) {
190
191
  isResolved = true;
@@ -192,7 +193,7 @@ class RabbitMq {
192
193
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
193
194
  }
194
195
  });
195
- this.connection.on('connectFailed', (err) => {
196
+ newConnection.on('connectFailed', (err) => {
196
197
  this.consumersTags = [];
197
198
  logger_1.default.error('rabbit: connection connectFailed', { err });
198
199
  if (!isResolved) {
@@ -201,8 +202,7 @@ class RabbitMq {
201
202
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
202
203
  }
203
204
  });
204
- this.connection.on('disconnect', ({ err }) => {
205
- // this.channel = null;
205
+ newConnection.on('disconnect', ({ err }) => {
206
206
  this.consumersTags = [];
207
207
  debug('rabbit: connection closed');
208
208
  if (this.options?.disableReconnect) {
@@ -213,24 +213,26 @@ class RabbitMq {
213
213
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
214
214
  }
215
215
  });
216
- this.connection.once('connect', async () => {
217
- debug('rabbit: connection established');
218
- this.creatingConnection = false;
219
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
216
+ newConnection.once('connect', async () => {
217
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
218
+ this.em.emit(consts_1.CONNECTION_CREATED_CONST, newConnection);
220
219
  isResolved = true;
221
- resolve(connection);
220
+ resolve(newConnection);
222
221
  });
223
222
  });
224
223
  }
225
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
224
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = consts_1.ConnectionPurpose.Consume, }) {
226
225
  let connection;
227
226
  try {
228
- connection = await this.getConnection();
227
+ connection = await this.getConnection(connectionPurpose);
229
228
  }
230
229
  catch (e) {
231
230
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
232
231
  throw e;
233
232
  }
233
+ if (!connection) {
234
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
235
+ }
234
236
  const channel = connection.createChannel({ ...options });
235
237
  (0, events_1.once)(channel, 'close').then((args) => {
236
238
  logger_1.default.error(`rabbit: channel ${name} closed`);
@@ -246,14 +248,15 @@ class RabbitMq {
246
248
  throw err;
247
249
  }
248
250
  }
249
- async assertChannel({ force = false } = {}) {
251
+ async assertChannel({ force = false, connectionPurpose = consts_1.ConnectionPurpose.Consume }) {
250
252
  if (!this.publishChannelSetupPromise) {
251
253
  this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
252
254
  if (this.channel && !force) {
253
255
  return resolve(this.channel);
254
256
  }
255
257
  try {
256
- const channel = await this.getNewChannel({});
258
+ const channel = await this.getNewChannel({ connectionPurpose });
259
+ debug('rabbit: new channel got', { connectionPurpose, channel: this.channel });
257
260
  channel.on('error', (err) => {
258
261
  logger_1.default.error('rabbit: channel error', { err });
259
262
  });
@@ -267,8 +270,8 @@ class RabbitMq {
267
270
  }
268
271
  return this.publishChannelSetupPromise;
269
272
  }
270
- async assertExchange(exchangeName, options) {
271
- const channel = await this.assertChannel();
273
+ async assertExchange(exchangeName, options = { connectionPurpose: consts_1.ConnectionPurpose.Consume }) {
274
+ const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
272
275
  if (this.exchanges[exchangeName]) {
273
276
  delete this.assertExchangePromises[exchangeName];
274
277
  return this.exchanges[exchangeName];
@@ -280,29 +283,30 @@ class RabbitMq {
280
283
  this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
281
284
  return this.exchanges[exchangeName];
282
285
  }
283
- async getQueueLength(queue) {
286
+ async getQueueLength(queue, connectionPurpose = consts_1.ConnectionPurpose.Consume) {
284
287
  RabbitMq.validateName('queue', queue);
288
+ const { connection } = this.connectionsMap[connectionPurpose];
285
289
  const { channel } = this;
286
290
  if (!channel) {
287
291
  throw new Error('channel is not defined');
288
292
  }
289
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
293
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
290
294
  return channel?.checkQueue(queue);
291
295
  }
292
- async deleteQueue(queue) {
296
+ async deleteQueue(queue, connectionPurpose) {
293
297
  RabbitMq.validateName('queue', queue);
294
- const channel = await this.assertChannel();
298
+ const channel = await this.assertChannel({ connectionPurpose });
295
299
  logger_1.default.info('rabbit: deleting queue', { queue });
296
300
  const deleteQueueRes = await channel.deleteQueue(queue);
297
301
  debug('queue deleted', deleteQueueRes);
298
302
  return deleteQueueRes;
299
303
  }
300
- async bindQueue(queue, exchange) {
301
- const channel = await this.assertChannel();
304
+ async bindQueue(queue, exchange, connectionPurpose) {
305
+ const channel = await this.assertChannel({ connectionPurpose });
302
306
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
303
307
  return channel.bindQueue(queue, exchange, '');
304
308
  }
305
- async setupQueue(queueName, options) {
309
+ async setupQueue(queueName, connectionPurpose, options) {
306
310
  let queue;
307
311
  const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
308
312
  const localeOptions = {
@@ -315,7 +319,7 @@ class RabbitMq {
315
319
  },
316
320
  };
317
321
  try {
318
- const channel = await this.assertChannel();
322
+ const channel = await this.assertChannel({ connectionPurpose });
319
323
  debug('assertQueue->channel.addSetup', { queueName });
320
324
  await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
321
325
  debug('assertQueue->channel.assertQueue', { queueName });
@@ -325,8 +329,8 @@ class RabbitMq {
325
329
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
326
330
  if (!this.options?.dontRetryAssert) {
327
331
  debug('retrying assertQueue', { queueName });
328
- const channel = await this.assertChannel({ force: true });
329
- await this.deleteQueue(queueName);
332
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
333
+ await this.deleteQueue(queueName, connectionPurpose);
330
334
  debug('retrying assertQueue->channel.addSetup', { queueName });
331
335
  await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
332
336
  debug('retrying assertQueue->channel.assertQueue', { queueName });
@@ -350,7 +354,7 @@ class RabbitMq {
350
354
  }
351
355
  return false;
352
356
  }
353
- async assertQueue(queueName, options) {
357
+ async assertQueue(queueName, connectionPurpose, options) {
354
358
  RabbitMq.validateName('queue', queueName);
355
359
  if (this.queues[queueName]) {
356
360
  delete this.queueSetupPromises[queueName];
@@ -359,7 +363,7 @@ class RabbitMq {
359
363
  if (this.queueSetupPromises[queueName]) {
360
364
  return this.queueSetupPromises[queueName];
361
365
  }
362
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
366
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
363
367
  return this.queueSetupPromises[queueName];
364
368
  }
365
369
  saveConsumer(queue, callback, options) {
@@ -402,9 +406,9 @@ class RabbitMq {
402
406
  }
403
407
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
404
408
  }
405
- const channel = await this.getNewChannel({});
409
+ const channel = await this.getNewChannel({ connectionPurpose: consts_1.ConnectionPurpose.Consume });
406
410
  return channel.addSetup(async (confirmChannel) => {
407
- const q = await this.assertQueue(queue, optionsWithDefaults);
411
+ const q = await this.assertQueue(queue, consts_1.ConnectionPurpose.Consume, optionsWithDefaults);
408
412
  await confirmChannel.prefetch(limit, false);
409
413
  const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
410
414
  if (!msg) {
@@ -486,7 +490,7 @@ class RabbitMq {
486
490
  RabbitMq.validateName('queue', queue);
487
491
  const { limit, deadMessageTtl } = optionsWithDefaults;
488
492
  await this.saveConsumer(queue, callback, options);
489
- const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
493
+ const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
490
494
  return channel.addSetup(async (c) => {
491
495
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
492
496
  await c.assertQueue(queue);
@@ -501,14 +505,14 @@ class RabbitMq {
501
505
  async publish(exchange, content, customHeaders) {
502
506
  return (0, utils_1.wrapSetImmediate)(async () => {
503
507
  RabbitMq.validateName('exchange', exchange);
504
- const channel = await this.assertChannel();
505
- await this.assertExchange(exchange);
508
+ const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
509
+ await this.assertExchange(exchange, { connectionPurpose: consts_1.ConnectionPurpose.Publish });
506
510
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
507
511
  });
508
512
  }
509
513
  async sendToQueue(queue, content, options, customHeaders) {
510
514
  try {
511
- await this.assertChannel();
515
+ await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
512
516
  }
513
517
  catch (e) {
514
518
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -516,7 +520,7 @@ class RabbitMq {
516
520
  }
517
521
  try {
518
522
  RabbitMq.validateName('queue', queue);
519
- await this.assertQueue(queue, options);
523
+ await this.assertQueue(queue, consts_1.ConnectionPurpose.Publish, options);
520
524
  }
521
525
  catch (e) {
522
526
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
@@ -528,31 +532,36 @@ class RabbitMq {
528
532
  return res;
529
533
  }
530
534
  catch (e) {
531
- const isConnected = await this.isConnected();
532
- logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
535
+ logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
533
536
  throw e;
534
537
  }
535
538
  }
536
539
  async isConnected() {
537
- const connection = await this.getConnection();
538
- const isConnected = connection.isConnected();
539
- if (!isConnected) {
540
- logger_1.default.error('rabbit: isConnected - false');
541
- return false;
542
- }
543
- const channel = await this.assertChannel();
544
- try {
545
- await Promise.all([
546
- channel.waitForConnect(),
547
- ...this.consumers.map((c) => channel.checkQueue(c.queue)),
548
- ]);
549
- }
550
- catch (e) {
551
- logger_1.default.error('rabbit: isConnected - false');
552
- return false;
553
- }
554
- logger_1.default.info('rabbit: isConnected - true');
555
- return true;
540
+ const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
541
+ const { connection } = connectionData;
542
+ if (connectionPurpose === consts_1.ConnectionPurpose.Publish && !connection) {
543
+ return true; // The connection hasn't been initialized yet, as no messages have been sent through it
544
+ }
545
+ const isConnected = connection?.isConnected();
546
+ if (!isConnected) {
547
+ logger_1.default.error('rabbit: isConnected - false');
548
+ return false;
549
+ }
550
+ const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
551
+ try {
552
+ await Promise.all([
553
+ channel.waitForConnect(),
554
+ ...this.consumers.map((c) => channel.checkQueue(c.queue)),
555
+ ]);
556
+ }
557
+ catch (e) {
558
+ logger_1.default.error('rabbit: isConnected - false');
559
+ return false;
560
+ }
561
+ logger_1.default.info('rabbit: isConnected - true');
562
+ return true;
563
+ }));
564
+ return isEachConnectionConnected.every((isConnected) => isConnected === true);
556
565
  }
557
566
  async gracefulShutdown(signal) {
558
567
  const tagsNumber = this.consumersTags.length;
@@ -571,3 +580,5 @@ class RabbitMq {
571
580
  }
572
581
  }
573
582
  exports.default = RabbitMq;
583
+ var celery_1 = require("./lib/celery");
584
+ Object.defineProperty(exports, "sendCeleryTaskViaHttp", { enumerable: true, get: function () { return celery_1.sendCeleryTaskViaHttp; } });
@@ -17,3 +17,7 @@ export declare const DEFAULT_OPTIONS: {
17
17
  auditContext: null;
18
18
  enableRabbitTrace: boolean;
19
19
  };
20
+ export declare enum ConnectionPurpose {
21
+ Consume = "consume",
22
+ Publish = "publish"
23
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_OPTIONS = exports.CONNECTION_FAILED_CONST = exports.CONNECTION_CREATED_CONST = exports.DEFAULT_USE_CONSUME_WITH_LOCK = exports.USER_OBJECT = exports.AUTOMATION_ID_HEADER = exports.USER_TRACING_HEADER = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
3
+ exports.ConnectionPurpose = exports.DEFAULT_OPTIONS = exports.CONNECTION_FAILED_CONST = exports.CONNECTION_CREATED_CONST = exports.DEFAULT_USE_CONSUME_WITH_LOCK = exports.USER_OBJECT = exports.AUTOMATION_ID_HEADER = exports.USER_TRACING_HEADER = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
4
4
  exports.DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 12;
5
5
  exports.DEFAULT_LOCK_TIMEOUT = 1000 * 5;
6
6
  exports.RETRY_HEADER = 'x-retry-count';
@@ -20,3 +20,8 @@ exports.DEFAULT_OPTIONS = {
20
20
  auditContext: null,
21
21
  enableRabbitTrace: false,
22
22
  };
23
+ var ConnectionPurpose;
24
+ (function (ConnectionPurpose) {
25
+ ConnectionPurpose["Consume"] = "consume";
26
+ ConnectionPurpose["Publish"] = "publish";
27
+ })(ConnectionPurpose = exports.ConnectionPurpose || (exports.ConnectionPurpose = {}));
@@ -1,3 +1,4 @@
1
+ import { AmqpConnectionManager } from 'amqp-connection-manager';
1
2
  import { ConsumeMessage, Options, Replies } from 'amqplib';
2
3
  export interface ExchangesCache {
3
4
  [key: string]: any;
@@ -41,3 +42,7 @@ export type AfConsumer = {
41
42
  options: ConsumeOptions | undefined;
42
43
  };
43
44
  export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
45
+ export type ConnectionData = {
46
+ connection: AmqpConnectionManager | null;
47
+ creatingConnection: boolean;
48
+ };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "3.2.23-beta.1.0",
3
+ "version": "3.2.24-beta.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
7
- "node": ">=15.0.0"
7
+ "node": ">=16.7.0"
8
8
  },
9
9
  "scripts": {
10
10
  "postinstall": "echo '\\033[0;31m\nWARNING: in case of migrating to new version of @autofleet/rabbit \nyou might have to delete existing queues or the deployment will fail.\\033[0m\n'",
package/src/index.ts CHANGED
@@ -18,11 +18,11 @@ import logger from './logger';
18
18
  import RabbitError from './lib/rabbitError';
19
19
  import getRedisInstance, { RedisConfig } from './lib/redis';
20
20
  import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
21
- import { sendCeleryTaskViaHttp } from './lib/celery';
22
21
  import {
23
22
  AUTOMATION_ID_HEADER,
24
23
  CONNECTION_CREATED_CONST,
25
24
  CONNECTION_FAILED_CONST,
25
+ ConnectionPurpose,
26
26
  DEFAULT_LOCK_TIMEOUT,
27
27
  DEFAULT_OPTIONS,
28
28
  RETRY_HEADER,
@@ -41,13 +41,13 @@ import {
41
41
  CONSUMER_DEFAULT_OPTIONS,
42
42
  QueueSetupPromisesDictionary,
43
43
  AssertExchangePromisesDictionary,
44
+ ConnectionData,
44
45
  } from './lib/types';
45
46
 
46
47
  // const debug = nodeDebug('af-rabbitmq')
47
48
  const debug = logger.debug.bind(logger);
48
49
 
49
50
  const PUBLISH_TIMEOUT = 1000 * 10;
50
-
51
51
  export interface IAfRabbitMq {
52
52
  ack: any;
53
53
  nack: any;
@@ -87,11 +87,13 @@ type newChannelOpts = {
87
87
  name?: string;
88
88
  onClose?: null | ((args: any | null) => void);
89
89
  options?: CreateChannelOpts | undefined;
90
+ connectionPurpose?: ConnectionPurpose,
90
91
  };
91
92
 
92
93
  type assertChannelOpts = {
93
94
  channelName?: string;
94
95
  force?: boolean;
96
+ connectionPurpose?: ConnectionPurpose;
95
97
  }
96
98
 
97
99
  type AfConsumer = {
@@ -103,7 +105,7 @@ type AfConsumer = {
103
105
  const HEARTBEAT = '60';
104
106
 
105
107
  class RabbitMq implements IAfRabbitMq {
106
- static parseMsg(msg: any) : any {
108
+ static parseMsg(msg: any): any {
107
109
  let { content } = msg;
108
110
  content = content.toString();
109
111
 
@@ -148,14 +150,15 @@ class RabbitMq implements IAfRabbitMq {
148
150
 
149
151
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
150
152
 
151
- blockReconnect: boolean | null | undefined
153
+ blockReconnect: boolean | null | undefined;
152
154
 
153
- connection: AmqpConnectionManager | null | undefined
155
+ connectionsMap: {
156
+ [ConnectionPurpose.Consume]: ConnectionData;
157
+ [ConnectionPurpose.Publish]: ConnectionData;
158
+ };
154
159
 
155
160
  em: EventEmitter;
156
161
 
157
- creatingConnection: boolean;
158
-
159
162
  exchanges: ExchangesCache;
160
163
 
161
164
  queues: QueuesCache;
@@ -179,8 +182,10 @@ class RabbitMq implements IAfRabbitMq {
179
182
  this.em = new EventEmitter();
180
183
  this.channel = null;
181
184
  this.publishChannelSetupPromise = null;
182
- this.connection = null;
183
- this.creatingConnection = false;
185
+ this.connectionsMap = {
186
+ [ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
187
+ [ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
188
+ };
184
189
  this.exchanges = {};
185
190
  this.queues = {};
186
191
  this.queueSetupPromises = {};
@@ -217,7 +222,7 @@ class RabbitMq implements IAfRabbitMq {
217
222
  return false;
218
223
  }
219
224
 
220
- public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
225
+ public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
221
226
  if (msg) {
222
227
  debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
223
228
  await channel.ack(msg);
@@ -243,8 +248,8 @@ class RabbitMq implements IAfRabbitMq {
243
248
  userMsg: ConsumeMessageOrNull,
244
249
  {
245
250
  skipRetry = false,
246
- }: NackOptions = { },
247
- ) : Promise<any> => {
251
+ }: NackOptions = {},
252
+ ): Promise<any> => {
248
253
  await this.unlockRedisIfNeeded(releaseLock);
249
254
  if (channel && msg) {
250
255
  if (
@@ -278,30 +283,31 @@ class RabbitMq implements IAfRabbitMq {
278
283
  }
279
284
  }
280
285
 
281
- async getConnection() {
282
- return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
286
+ async getConnection(connectionPurpose: ConnectionPurpose) {
287
+ return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
288
+ const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
283
289
  if (this.blockReconnect) {
284
290
  debug('rabbit: block reconnect');
285
291
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
286
292
  // @ts-ignore
287
293
  return resolve();
288
294
  }
289
- if (this.connection !== null) {
290
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
295
+ if (connection !== null) {
296
+ if (this.options?.disableReconnect || connection?.isConnected()) {
291
297
  debug('rabbit: connection - is connected');
292
298
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
293
299
  // @ts-ignore
294
- return resolve(this.connection);
300
+ return resolve(connection);
295
301
  }
296
302
  debug('rabbit: connection - reconnecting');
297
303
  }
298
- if (this.creatingConnection) {
304
+ if (creatingConnection) {
299
305
  debug('rabbit: creating connection emi');
300
306
  this.em.once(CONNECTION_CREATED_CONST, resolve);
301
307
  this.em.once(CONNECTION_FAILED_CONST, reject);
302
308
  return;
303
309
  }
304
- this.creatingConnection = true;
310
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
305
311
  let isResolved = false;
306
312
 
307
313
  // It is import to use it as a function and not as a variable
@@ -318,12 +324,13 @@ class RabbitMq implements IAfRabbitMq {
318
324
  };
319
325
 
320
326
  const defaultUrls = findServers();
321
- const connection: AmqpConnectionManager = await connect(defaultUrls, {
327
+ const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
322
328
  findServers,
323
329
  });
324
330
 
325
- this.connection = connection;
326
- this.connection.on('error', (err) => {
331
+ this.connectionsMap[connectionPurpose].connection = newConnection;
332
+
333
+ newConnection.on('error', (err) => {
327
334
  logger.error('rabbit: connection error', { err });
328
335
  if (!isResolved) {
329
336
  isResolved = true;
@@ -332,7 +339,7 @@ class RabbitMq implements IAfRabbitMq {
332
339
  }
333
340
  });
334
341
 
335
- this.connection.on('connectFailed', (err) => {
342
+ newConnection.on('connectFailed', (err) => {
336
343
  this.consumersTags = [];
337
344
  logger.error('rabbit: connection connectFailed', { err });
338
345
  if (!isResolved) {
@@ -342,8 +349,7 @@ class RabbitMq implements IAfRabbitMq {
342
349
  }
343
350
  });
344
351
 
345
- this.connection.on('disconnect', ({ err }) => {
346
- // this.channel = null;
352
+ newConnection.on('disconnect', ({ err }) => {
347
353
  this.consumersTags = [];
348
354
  debug('rabbit: connection closed');
349
355
  if (this.options?.disableReconnect) {
@@ -353,25 +359,28 @@ class RabbitMq implements IAfRabbitMq {
353
359
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
354
360
  }
355
361
  });
356
-
357
- this.connection.once('connect', async () => {
358
- debug('rabbit: connection established');
359
- this.creatingConnection = false;
360
- this.em.emit(CONNECTION_CREATED_CONST, connection);
362
+ newConnection.once('connect', async () => {
363
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
364
+ this.em.emit(CONNECTION_CREATED_CONST, newConnection);
361
365
  isResolved = true;
362
- resolve(connection);
366
+ resolve(newConnection);
363
367
  });
364
368
  });
365
369
  }
366
370
 
367
- async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
368
- let connection!: AmqpConnectionManager;
371
+ async getNewChannel({
372
+ name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
373
+ }: newChannelOpts): Promise<ChannelWrapper> {
374
+ let connection!: AmqpConnectionManager | undefined | null;
369
375
  try {
370
- connection = await this.getConnection();
376
+ connection = await this.getConnection(connectionPurpose);
371
377
  } catch (e) {
372
378
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
373
379
  throw e;
374
380
  }
381
+ if (!connection) {
382
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
383
+ }
375
384
  const channel = connection.createChannel({ ...options });
376
385
  once(channel, 'close').then((args) => {
377
386
  logger.error(`rabbit: channel ${name} closed`);
@@ -387,7 +396,7 @@ class RabbitMq implements IAfRabbitMq {
387
396
  }
388
397
  }
389
398
 
390
- async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
399
+ async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
391
400
  if (!this.publishChannelSetupPromise) {
392
401
  this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
393
402
  if (this.channel && !force) {
@@ -395,7 +404,8 @@ class RabbitMq implements IAfRabbitMq {
395
404
  }
396
405
 
397
406
  try {
398
- const channel = await this.getNewChannel({});
407
+ const channel = await this.getNewChannel({ connectionPurpose });
408
+ debug('rabbit: new channel got', { connectionPurpose, channel: this.channel });
399
409
  channel.on('error', (err) => {
400
410
  logger.error('rabbit: channel error', { err });
401
411
  });
@@ -409,9 +419,8 @@ class RabbitMq implements IAfRabbitMq {
409
419
  return this.publishChannelSetupPromise;
410
420
  }
411
421
 
412
- async assertExchange(exchangeName: string, options?: any) {
413
- const channel: ChannelWrapper = await this.assertChannel();
414
-
422
+ async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
423
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
415
424
  if (this.exchanges[exchangeName]) {
416
425
  delete this.assertExchangePromises[exchangeName];
417
426
  return this.exchanges[exchangeName];
@@ -426,32 +435,33 @@ class RabbitMq implements IAfRabbitMq {
426
435
  return this.exchanges[exchangeName];
427
436
  }
428
437
 
429
- async getQueueLength(queue: string) {
438
+ async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
430
439
  RabbitMq.validateName('queue', queue);
440
+ const { connection } = this.connectionsMap[connectionPurpose];
431
441
  const { channel } = this;
432
442
  if (!channel) {
433
443
  throw new Error('channel is not defined');
434
444
  }
435
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
445
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
436
446
  return channel?.checkQueue(queue);
437
447
  }
438
448
 
439
- private async deleteQueue(queue: string) {
449
+ private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
440
450
  RabbitMq.validateName('queue', queue);
441
- const channel: ChannelWrapper = await this.assertChannel();
451
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
442
452
  logger.info('rabbit: deleting queue', { queue });
443
453
  const deleteQueueRes = await channel.deleteQueue(queue);
444
454
  debug('queue deleted', deleteQueueRes);
445
455
  return deleteQueueRes;
446
456
  }
447
457
 
448
- async bindQueue(queue: string, exchange: string) {
449
- const channel: ChannelWrapper = await this.assertChannel();
458
+ async bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose) {
459
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
450
460
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
451
461
  return channel.bindQueue(queue, exchange, '');
452
462
  }
453
463
 
454
- async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
464
+ async setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
455
465
  let queue: Replies.AssertQueue;
456
466
  const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
457
467
  const localeOptions = {
@@ -464,7 +474,7 @@ class RabbitMq implements IAfRabbitMq {
464
474
  },
465
475
  };
466
476
  try {
467
- const channel: ChannelWrapper = await this.assertChannel();
477
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
468
478
  debug('assertQueue->channel.addSetup', { queueName });
469
479
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
470
480
  debug('assertQueue->channel.assertQueue', { queueName });
@@ -473,8 +483,8 @@ class RabbitMq implements IAfRabbitMq {
473
483
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
474
484
  if (!this.options?.dontRetryAssert) {
475
485
  debug('retrying assertQueue', { queueName });
476
- const channel = await this.assertChannel({ force: true });
477
- await this.deleteQueue(queueName);
486
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
487
+ await this.deleteQueue(queueName, connectionPurpose);
478
488
 
479
489
  debug('retrying assertQueue->channel.addSetup', { queueName });
480
490
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
@@ -504,7 +514,7 @@ class RabbitMq implements IAfRabbitMq {
504
514
  return false;
505
515
  }
506
516
 
507
- async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
517
+ async assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue) {
508
518
  RabbitMq.validateName('queue', queueName);
509
519
  if (this.queues[queueName]) {
510
520
  delete this.queueSetupPromises[queueName];
@@ -515,12 +525,12 @@ class RabbitMq implements IAfRabbitMq {
515
525
  return this.queueSetupPromises[queueName];
516
526
  }
517
527
 
518
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
528
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
519
529
  return this.queueSetupPromises[queueName];
520
530
  }
521
531
 
522
532
  private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
523
- const isConsumerExist :boolean = this.consumers.some((consumer) => consumer.queue === queue);
533
+ const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
524
534
  if (!isConsumerExist) {
525
535
  logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
526
536
  this.consumers.push({
@@ -569,9 +579,9 @@ class RabbitMq implements IAfRabbitMq {
569
579
  }
570
580
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
571
581
  }
572
- const channel = await this.getNewChannel({});
582
+ const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
573
583
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
574
- const q = await this.assertQueue(queue, optionsWithDefaults);
584
+ const q = await this.assertQueue(queue, ConnectionPurpose.Consume, optionsWithDefaults);
575
585
  await confirmChannel.prefetch(limit, false);
576
586
  const { consumerTag } = await confirmChannel.consume(
577
587
  queue,
@@ -659,13 +669,13 @@ class RabbitMq implements IAfRabbitMq {
659
669
  });
660
670
  }
661
671
 
662
- async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
672
+ async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
663
673
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
664
674
  RabbitMq.validateName('exchange', exchange);
665
675
  RabbitMq.validateName('queue', queue);
666
676
  const { limit, deadMessageTtl } = optionsWithDefaults;
667
677
  await this.saveConsumer(queue, callback, options);
668
- const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
678
+ const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
669
679
 
670
680
  return channel.addSetup(async (c: ConfirmChannel) => {
671
681
  const assertExchange = await assertExchangeFanout(c, exchange);
@@ -683,11 +693,11 @@ class RabbitMq implements IAfRabbitMq {
683
693
  });
684
694
  }
685
695
 
686
- async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
696
+ async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
687
697
  return wrapSetImmediate(async () => {
688
698
  RabbitMq.validateName('exchange', exchange);
689
- const channel: ChannelWrapper = await this.assertChannel();
690
- await this.assertExchange(exchange);
699
+ const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
700
+ await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
691
701
  await channel.publish(exchange, '',
692
702
  Buffer.from(JSON.stringify(content)),
693
703
  RabbitMq.getPublishOptions(customHeaders));
@@ -701,7 +711,7 @@ class RabbitMq implements IAfRabbitMq {
701
711
  customHeaders?: any,
702
712
  ): Promise<boolean | undefined> {
703
713
  try {
704
- await this.assertChannel();
714
+ await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
705
715
  } catch (e) {
706
716
  logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
707
717
  throw e;
@@ -709,7 +719,7 @@ class RabbitMq implements IAfRabbitMq {
709
719
 
710
720
  try {
711
721
  RabbitMq.validateName('queue', queue);
712
- await this.assertQueue(queue, options);
722
+ await this.assertQueue(queue, ConnectionPurpose.Publish, options);
713
723
  } catch (e) {
714
724
  logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
715
725
  throw e;
@@ -722,34 +732,41 @@ class RabbitMq implements IAfRabbitMq {
722
732
  debug(`rabbit: sending to queue ${queue}`, { res });
723
733
  return res;
724
734
  } catch (e) {
725
- const isConnected = await this.isConnected();
726
- logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
735
+ logger.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
727
736
  throw e;
728
737
  }
729
738
  }
730
739
 
731
- async isConnected() : Promise<boolean> {
732
- const connection = await this.getConnection();
733
- const isConnected = connection.isConnected();
734
- if (!isConnected) {
735
- logger.error('rabbit: isConnected - false');
736
- return false;
737
- }
738
- const channel: any = await this.assertChannel();
739
- try {
740
- await Promise.all([
741
- channel.waitForConnect(),
742
- ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
743
- ]);
744
- } catch (e) {
745
- logger.error('rabbit: isConnected - false');
746
- return false;
747
- }
748
- logger.info('rabbit: isConnected - true');
749
- return true;
740
+ async isConnected(): Promise<boolean> {
741
+ const isEachConnectionConnected = await Promise.all(
742
+ Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
743
+ const { connection } = connectionData;
744
+ if (connectionPurpose === ConnectionPurpose.Publish && !connection) {
745
+ return true; // The connection hasn't been initialized yet, as no messages have been sent through it
746
+ }
747
+ const isConnected = connection?.isConnected();
748
+ if (!isConnected) {
749
+ logger.error('rabbit: isConnected - false');
750
+ return false;
751
+ }
752
+ const channel: any = await this.assertChannel({ connectionPurpose: connectionPurpose as ConnectionPurpose });
753
+ try {
754
+ await Promise.all([
755
+ channel.waitForConnect(),
756
+ ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
757
+ ]);
758
+ } catch (e) {
759
+ logger.error('rabbit: isConnected - false');
760
+ return false;
761
+ }
762
+ logger.info('rabbit: isConnected - true');
763
+ return true;
764
+ }),
765
+ );
766
+ return isEachConnectionConnected.every((isConnected) => isConnected === true);
750
767
  }
751
768
 
752
- async gracefulShutdown(signal: string) : Promise<void> {
769
+ async gracefulShutdown(signal: string): Promise<void> {
753
770
  const tagsNumber = this.consumersTags.length;
754
771
  logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
755
772
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
@@ -769,4 +786,4 @@ export default RabbitMq;
769
786
 
770
787
  export {
771
788
  sendCeleryTaskViaHttp,
772
- };
789
+ } from './lib/celery';
package/src/lib/celery.ts CHANGED
@@ -2,9 +2,9 @@ import { randomUUID } from 'node:crypto';
2
2
  import logger from '../logger';
3
3
  // Environment configuration
4
4
  const config = {
5
- host: process.env.RABBITMQ_SERVICE_HOST || 'localhost',
6
- username: process.env.RABBITMQ_USERNAME || 'guest',
7
- password: process.env.RABBITMQ_PASSWORD || 'guest',
5
+ host: process.env.RABBITMQ_SERVICE_HOST || 'localhost',
6
+ username: process.env.RABBITMQ_USERNAME || 'guest',
7
+ password: process.env.RABBITMQ_PASSWORD || 'guest',
8
8
  } as const;
9
9
 
10
10
  // Type definitions
@@ -42,48 +42,48 @@ interface SendTaskOptions {
42
42
  }
43
43
 
44
44
  async function sendCeleryTaskViaHttp(
45
- data: TaskData,
46
- { taskName, queueName }: SendTaskOptions,
45
+ data: TaskData,
46
+ { taskName, queueName }: SendTaskOptions,
47
47
  ): Promise<void> {
48
- const apiUrl = `http://${config.host}:15672/api/exchanges/%2f/amq.default/publish`;
48
+ const apiUrl = `http://${config.host}:15672/api/exchanges/%2f/amq.default/publish`;
49
49
 
50
- const message: TaskMessage = {
51
- task: taskName,
52
- id: randomUUID(),
53
- args: [data],
54
- };
50
+ const message: TaskMessage = {
51
+ task: taskName,
52
+ id: randomUUID(),
53
+ args: [data],
54
+ };
55
55
 
56
- const payload: PublishPayload = {
57
- properties: {
58
- delivery_mode: 2,
59
- content_type: 'application/json',
60
- },
61
- routing_key: queueName,
62
- payload: JSON.stringify(message),
63
- payload_encoding: 'string',
64
- };
56
+ const payload: PublishPayload = {
57
+ properties: {
58
+ delivery_mode: 2,
59
+ content_type: 'application/json',
60
+ },
61
+ routing_key: queueName,
62
+ payload: JSON.stringify(message),
63
+ payload_encoding: 'string',
64
+ };
65
65
 
66
- try {
67
- const response = await fetch(apiUrl, {
68
- method: 'POST',
69
- headers: {
70
- 'Content-Type': 'application/json',
71
- Authorization: `Basic ${Buffer.from(`${config.username}:${config.password}`).toString('base64')}`,
72
- },
73
- body: JSON.stringify(payload),
74
- });
66
+ try {
67
+ const response = await fetch(apiUrl, {
68
+ method: 'POST',
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ Authorization: `Basic ${Buffer.from(`${config.username}:${config.password}`).toString('base64')}`,
72
+ },
73
+ body: JSON.stringify(payload),
74
+ });
75
75
 
76
- if (response.ok) {
77
- const result: PublishResponse = await response.json();
78
- logger.info('Successfully published message:', result);
79
- } else {
80
- logger.error(`Failed to publish message. Status code: ${response.status}`);
81
- logger.error(`Response: ${await response.text()}`);
82
- }
83
- } catch (error) {
84
- logger.error('Error sending request:', error instanceof Error ? error.message : String(error));
85
- throw error;
76
+ if (response.ok) {
77
+ const result: PublishResponse = await response.json();
78
+ logger.info('Successfully published message:', result);
79
+ } else {
80
+ logger.error(`Failed to publish message. Status code: ${response.status}`);
81
+ logger.error(`Response: ${await response.text()}`);
86
82
  }
83
+ } catch (error) {
84
+ logger.error('Error sending request:', error instanceof Error ? error.message : String(error));
85
+ throw error;
86
+ }
87
87
  }
88
88
 
89
89
  export { sendCeleryTaskViaHttp, TaskData, SendTaskOptions };
package/src/lib/consts.ts CHANGED
@@ -17,3 +17,8 @@ export const DEFAULT_OPTIONS = {
17
17
  auditContext: null,
18
18
  enableRabbitTrace: false,
19
19
  };
20
+
21
+ export enum ConnectionPurpose {
22
+ Consume = 'consume',
23
+ Publish = 'publish',
24
+ }
package/src/lib/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { AmqpConnectionManager } from 'amqp-connection-manager';
1
2
  import { ConsumeMessage, Options, Replies } from 'amqplib';
2
3
 
3
4
  export interface ExchangesCache {
@@ -59,3 +60,8 @@ export const CONSUMER_DEFAULT_OPTIONS: Options.Consume = {
59
60
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
60
61
  },
61
62
  };
63
+
64
+ export type ConnectionData = {
65
+ connection: AmqpConnectionManager | null;
66
+ creatingConnection: boolean;
67
+ };