@autofleet/rabbit 3.2.22-beta.4 → 3.2.22

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,7 +3,7 @@ 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 { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, ConnectionPurpose, ConnectionData } from './lib/types';
6
+ import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary } from './lib/types';
7
7
  export interface IAfRabbitMq {
8
8
  ack: any;
9
9
  nack: any;
@@ -37,12 +37,10 @@ type newChannelOpts = {
37
37
  name?: string;
38
38
  onClose?: null | ((args: any | null) => void);
39
39
  options?: CreateChannelOpts | undefined;
40
- connectionPurpose?: ConnectionPurpose;
41
40
  };
42
41
  type assertChannelOpts = {
43
42
  channelName?: string;
44
43
  force?: boolean;
45
- connectionPurpose?: ConnectionPurpose;
46
44
  };
47
45
  declare class RabbitMq implements IAfRabbitMq {
48
46
  static parseMsg(msg: any): any;
@@ -62,14 +60,13 @@ declare class RabbitMq implements IAfRabbitMq {
62
60
  channel: ChannelWrapper | null;
63
61
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
64
62
  blockReconnect: boolean | null | undefined;
65
- connectionsMap: {
66
- [ConnectionPurpose.Consume]: ConnectionData;
67
- [ConnectionPurpose.Publish]: ConnectionData;
68
- };
63
+ connection: AmqpConnectionManager | null | undefined;
69
64
  em: EventEmitter;
65
+ creatingConnection: boolean;
70
66
  exchanges: ExchangesCache;
71
67
  queues: QueuesCache;
72
68
  queueSetupPromises: QueueSetupPromisesDictionary;
69
+ assertExchangePromises: AssertExchangePromisesDictionary;
73
70
  options: AfRabbitOptions | undefined;
74
71
  redisClient: any;
75
72
  redisLock?: RedisLockType;
@@ -80,15 +77,16 @@ declare class RabbitMq implements IAfRabbitMq {
80
77
  private shouldConsumeMessageByTimestamp;
81
78
  ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
82
79
  nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
83
- getConnection(connectionPurpose: ConnectionPurpose): Promise<AmqpConnectionManager | null | undefined>;
84
- getNewChannel({ name, onClose, options, connectionPurpose, }: newChannelOpts): Promise<ChannelWrapper>;
85
- assertChannel({ force, connectionPurpose }: assertChannelOpts): Promise<ChannelWrapper>;
80
+ getConnection(): Promise<AmqpConnectionManager>;
81
+ getNewChannel({ name, onClose, options }?: newChannelOpts): Promise<ChannelWrapper>;
82
+ assertChannel({ force }?: assertChannelOpts): Promise<ChannelWrapper>;
86
83
  assertExchange(exchangeName: string, options?: any): Promise<any>;
87
- getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
84
+ getQueueLength(queue: string): Promise<Replies.AssertQueue>;
88
85
  private deleteQueue;
89
- bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose): Promise<void>;
90
- setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
91
- assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<any>;
86
+ bindQueue(queue: string, exchange: string): Promise<void>;
87
+ setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
88
+ static shouldUseQuorum(queueName: string): boolean;
89
+ assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
92
90
  private saveConsumer;
93
91
  consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
94
92
  private lockRedisIfNeeded;
package/dist/index.js CHANGED
@@ -118,13 +118,12 @@ class RabbitMq {
118
118
  this.em = new events_1.EventEmitter();
119
119
  this.channel = null;
120
120
  this.publishChannelSetupPromise = null;
121
- this.connectionsMap = {
122
- [types_1.ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
123
- [types_1.ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
124
- };
121
+ this.connection = null;
122
+ this.creatingConnection = false;
125
123
  this.exchanges = {};
126
124
  this.queues = {};
127
125
  this.queueSetupPromises = {};
126
+ this.assertExchangePromises = {};
128
127
  this.consumers = [];
129
128
  this.options = options;
130
129
  this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
@@ -142,31 +141,30 @@ class RabbitMq {
142
141
  });
143
142
  }
144
143
  }
145
- async getConnection(connectionPurpose) {
144
+ async getConnection() {
146
145
  return new Promise(async (resolve, reject) => {
147
- const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
148
146
  if (this.blockReconnect) {
149
147
  debug('rabbit: block reconnect');
150
148
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
151
149
  // @ts-ignore
152
150
  return resolve();
153
151
  }
154
- if (connection !== null) {
155
- if (this.options?.disableReconnect || connection?.isConnected()) {
152
+ if (this.connection !== null) {
153
+ if (this.options?.disableReconnect || this.connection?.isConnected()) {
156
154
  debug('rabbit: connection - is connected');
157
155
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
158
156
  // @ts-ignore
159
- return resolve(connection);
157
+ return resolve(this.connection);
160
158
  }
161
159
  debug('rabbit: connection - reconnecting');
162
160
  }
163
- if (creatingConnection) {
161
+ if (this.creatingConnection) {
164
162
  debug('rabbit: creating connection emi');
165
163
  this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
166
164
  this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
167
165
  return;
168
166
  }
169
- this.connectionsMap[connectionPurpose].creatingConnection = true;
167
+ this.creatingConnection = true;
170
168
  let isResolved = false;
171
169
  // It is import to use it as a function and not as a variable
172
170
  // because of k8s changes the env variables
@@ -179,11 +177,11 @@ class RabbitMq {
179
177
  return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
180
178
  };
181
179
  const defaultUrls = findServers();
182
- const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
180
+ const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
183
181
  findServers,
184
182
  });
185
- this.connectionsMap[connectionPurpose].connection = newConnection;
186
- newConnection.on('error', (err) => {
183
+ this.connection = connection;
184
+ this.connection.on('error', (err) => {
187
185
  logger_1.default.error('rabbit: connection error', { err });
188
186
  if (!isResolved) {
189
187
  isResolved = true;
@@ -191,7 +189,7 @@ class RabbitMq {
191
189
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
192
190
  }
193
191
  });
194
- newConnection.on('connectFailed', (err) => {
192
+ this.connection.on('connectFailed', (err) => {
195
193
  this.consumersTags = [];
196
194
  logger_1.default.error('rabbit: connection connectFailed', { err });
197
195
  if (!isResolved) {
@@ -200,7 +198,8 @@ class RabbitMq {
200
198
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
201
199
  }
202
200
  });
203
- newConnection.on('disconnect', ({ err }) => {
201
+ this.connection.on('disconnect', ({ err }) => {
202
+ // this.channel = null;
204
203
  this.consumersTags = [];
205
204
  debug('rabbit: connection closed');
206
205
  if (this.options?.disableReconnect) {
@@ -211,27 +210,24 @@ class RabbitMq {
211
210
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
212
211
  }
213
212
  });
214
- newConnection.once('connect', async () => {
213
+ this.connection.once('connect', async () => {
215
214
  debug('rabbit: connection established');
216
- this.connectionsMap[connectionPurpose].creatingConnection = false;
217
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, newConnection);
215
+ this.creatingConnection = false;
216
+ this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
218
217
  isResolved = true;
219
- resolve(newConnection);
218
+ resolve(connection);
220
219
  });
221
220
  });
222
221
  }
223
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = types_1.ConnectionPurpose.Consume, }) {
222
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
224
223
  let connection;
225
224
  try {
226
- connection = await this.getConnection(connectionPurpose);
225
+ connection = await this.getConnection();
227
226
  }
228
227
  catch (e) {
229
228
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
230
229
  throw e;
231
230
  }
232
- if (!connection) {
233
- throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
234
- }
235
231
  const channel = connection.createChannel({ ...options });
236
232
  (0, events_1.once)(channel, 'close').then((args) => {
237
233
  logger_1.default.error(`rabbit: channel ${name} closed`);
@@ -247,14 +243,14 @@ class RabbitMq {
247
243
  throw err;
248
244
  }
249
245
  }
250
- async assertChannel({ force = false, connectionPurpose = types_1.ConnectionPurpose.Consume }) {
246
+ async assertChannel({ force = false } = {}) {
251
247
  if (!this.publishChannelSetupPromise) {
252
248
  this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
253
249
  if (this.channel && !force) {
254
250
  return resolve(this.channel);
255
251
  }
256
252
  try {
257
- const channel = await this.getNewChannel({ connectionPurpose });
253
+ const channel = await this.getNewChannel({});
258
254
  channel.on('error', (err) => {
259
255
  logger_1.default.error('rabbit: channel error', { err });
260
256
  });
@@ -268,66 +264,90 @@ class RabbitMq {
268
264
  }
269
265
  return this.publishChannelSetupPromise;
270
266
  }
271
- async assertExchange(exchangeName, options = { connectionPurpose: types_1.ConnectionPurpose.Consume }) {
272
- const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
267
+ async assertExchange(exchangeName, options) {
268
+ const channel = await this.assertChannel();
273
269
  if (this.exchanges[exchangeName]) {
270
+ delete this.assertExchangePromises[exchangeName];
274
271
  return this.exchanges[exchangeName];
275
272
  }
276
- const exchange = await (0, utils_1.assertExchangeFanout)(channel, exchangeName);
277
- this.exchanges[exchangeName] = exchange;
278
- return exchange;
273
+ if (this.assertExchangePromises[exchangeName]) {
274
+ return this.assertExchangePromises[exchangeName];
275
+ }
276
+ this.assertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
277
+ this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
278
+ return this.exchanges[exchangeName];
279
279
  }
280
- async getQueueLength(queue, connectionPurpose = types_1.ConnectionPurpose.Consume) {
280
+ async getQueueLength(queue) {
281
281
  RabbitMq.validateName('queue', queue);
282
- const { connection } = this.connectionsMap[connectionPurpose];
283
282
  const { channel } = this;
284
283
  if (!channel) {
285
284
  throw new Error('channel is not defined');
286
285
  }
287
- debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
286
+ debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
288
287
  return channel?.checkQueue(queue);
289
288
  }
290
- async deleteQueue(queue, connectionPurpose) {
289
+ async deleteQueue(queue) {
291
290
  RabbitMq.validateName('queue', queue);
292
- const channel = await this.assertChannel({ connectionPurpose });
291
+ const channel = await this.assertChannel();
293
292
  logger_1.default.info('rabbit: deleting queue', { queue });
294
293
  const deleteQueueRes = await channel.deleteQueue(queue);
295
294
  debug('queue deleted', deleteQueueRes);
296
295
  return deleteQueueRes;
297
296
  }
298
- async bindQueue(queue, exchange, connectionPurpose) {
299
- const channel = await this.assertChannel({ connectionPurpose });
297
+ async bindQueue(queue, exchange) {
298
+ const channel = await this.assertChannel();
300
299
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
301
300
  return channel.bindQueue(queue, exchange, '');
302
301
  }
303
- async setupQueue(queueName, connectionPurpose, options) {
302
+ async setupQueue(queueName, options) {
304
303
  let queue;
304
+ const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
305
+ const localeOptions = {
306
+ ...options,
307
+ durable: true,
308
+ arguments: {
309
+ ...options?.arguments,
310
+ 'x-consumer-timeout': 1000 * 60 * 60 * 24,
311
+ 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
312
+ },
313
+ };
305
314
  try {
306
- const channel = await this.assertChannel({ connectionPurpose });
315
+ const channel = await this.assertChannel();
307
316
  debug('assertQueue->channel.addSetup', { queueName });
308
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
317
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
309
318
  debug('assertQueue->channel.assertQueue', { queueName });
310
- queue = await channel.assertQueue(queueName, options);
319
+ queue = await channel.assertQueue(queueName, localeOptions);
311
320
  }
312
321
  catch (e) {
313
322
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
314
323
  if (!this.options?.dontRetryAssert) {
315
324
  debug('retrying assertQueue', { queueName });
316
- const channel = await this.assertChannel({ force: true, connectionPurpose });
317
- await this.deleteQueue(queueName, connectionPurpose);
325
+ const channel = await this.assertChannel({ force: true });
326
+ await this.deleteQueue(queueName);
318
327
  debug('retrying assertQueue->channel.addSetup', { queueName });
319
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
328
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
320
329
  debug('retrying assertQueue->channel.assertQueue', { queueName });
321
- queue = await channel.assertQueue(queueName, options);
330
+ queue = await channel.assertQueue(queueName, localeOptions);
322
331
  }
323
332
  else {
324
333
  throw e;
325
334
  }
326
335
  }
327
- this.queues[queueName] = queueName;
336
+ this.queues[queueName] = queue;
328
337
  return queue;
329
338
  }
330
- async assertQueue(queueName, connectionPurpose, options) {
339
+ static shouldUseQuorum(queueName) {
340
+ const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
341
+ if (envQuorumQueuesWhitelist === '*') {
342
+ return true;
343
+ }
344
+ if (envQuorumQueuesWhitelist) {
345
+ const whitelist = envQuorumQueuesWhitelist.split(',');
346
+ return whitelist.includes(queueName);
347
+ }
348
+ return false;
349
+ }
350
+ async assertQueue(queueName, options) {
331
351
  RabbitMq.validateName('queue', queueName);
332
352
  if (this.queues[queueName]) {
333
353
  delete this.queueSetupPromises[queueName];
@@ -336,7 +356,7 @@ class RabbitMq {
336
356
  if (this.queueSetupPromises[queueName]) {
337
357
  return this.queueSetupPromises[queueName];
338
358
  }
339
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
359
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
340
360
  return this.queueSetupPromises[queueName];
341
361
  }
342
362
  saveConsumer(queue, callback, options) {
@@ -379,16 +399,17 @@ class RabbitMq {
379
399
  }
380
400
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
381
401
  }
382
- const channel = await this.getNewChannel({ connectionPurpose: types_1.ConnectionPurpose.Consume });
402
+ const channel = await this.getNewChannel({});
383
403
  return channel.addSetup(async (confirmChannel) => {
384
- await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
385
- await confirmChannel.prefetch(limit, true);
404
+ const q = await this.assertQueue(queue, optionsWithDefaults);
405
+ await confirmChannel.prefetch(limit, false);
386
406
  const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
387
407
  if (!msg) {
388
408
  return null;
389
409
  }
390
410
  const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
391
411
  const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
412
+ const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
392
413
  const parsedMessage = RabbitMq.parseMsg(msg);
393
414
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
394
415
  const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
@@ -413,7 +434,10 @@ class RabbitMq {
413
434
  outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
414
435
  }
415
436
  if (auditContext) {
416
- await auditContext(queue);
437
+ await auditContext(queue, {
438
+ userId,
439
+ automationId,
440
+ });
417
441
  }
418
442
  const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
419
443
  if (!shouldConsume) {
@@ -459,7 +483,7 @@ class RabbitMq {
459
483
  RabbitMq.validateName('queue', queue);
460
484
  const { limit, deadMessageTtl } = optionsWithDefaults;
461
485
  await this.saveConsumer(queue, callback, options);
462
- const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
486
+ const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
463
487
  return channel.addSetup(async (c) => {
464
488
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
465
489
  await c.assertQueue(queue);
@@ -474,14 +498,14 @@ class RabbitMq {
474
498
  async publish(exchange, content, customHeaders) {
475
499
  return (0, utils_1.wrapSetImmediate)(async () => {
476
500
  RabbitMq.validateName('exchange', exchange);
477
- const channel = await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
478
- await this.assertExchange(exchange, { connectionPurpose: types_1.ConnectionPurpose.Publish });
501
+ const channel = await this.assertChannel();
502
+ await this.assertExchange(exchange);
479
503
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
480
504
  });
481
505
  }
482
506
  async sendToQueue(queue, content, options, customHeaders) {
483
507
  try {
484
- await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
508
+ await this.assertChannel();
485
509
  }
486
510
  catch (e) {
487
511
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -489,7 +513,7 @@ class RabbitMq {
489
513
  }
490
514
  try {
491
515
  RabbitMq.validateName('queue', queue);
492
- await this.assertQueue(queue, types_1.ConnectionPurpose.Publish, options);
516
+ await this.assertQueue(queue, options);
493
517
  }
494
518
  catch (e) {
495
519
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
@@ -501,36 +525,31 @@ class RabbitMq {
501
525
  return res;
502
526
  }
503
527
  catch (e) {
504
- logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
528
+ const isConnected = await this.isConnected();
529
+ logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
505
530
  throw e;
506
531
  }
507
532
  }
508
533
  async isConnected() {
509
- const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
510
- const { connection } = connectionData;
511
- if (connectionPurpose === types_1.ConnectionPurpose.Publish && !connection) {
512
- return true; // The connection hasn't been initialized yet, as no messages have been sent through it
513
- }
514
- const isConnected = connection?.isConnected();
515
- if (!isConnected) {
516
- logger_1.default.error('rabbit: isConnected - false');
517
- return false;
518
- }
519
- const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
520
- try {
521
- await Promise.all([
522
- channel.waitForConnect(),
523
- ...this.consumers.map((c) => channel.checkQueue(c.queue)),
524
- ]);
525
- }
526
- catch (e) {
527
- logger_1.default.error('rabbit: isConnected - false');
528
- return false;
529
- }
530
- logger_1.default.info('rabbit: isConnected - true');
531
- return true;
532
- }));
533
- return isEachConnectionConnected.every((isConnected) => isConnected === true);
534
+ const connection = await this.getConnection();
535
+ const isConnected = connection.isConnected();
536
+ if (!isConnected) {
537
+ logger_1.default.error('rabbit: isConnected - false');
538
+ return false;
539
+ }
540
+ const channel = await this.assertChannel();
541
+ try {
542
+ await Promise.all([
543
+ channel.waitForConnect(),
544
+ ...this.consumers.map((c) => channel.checkQueue(c.queue)),
545
+ ]);
546
+ }
547
+ catch (e) {
548
+ logger_1.default.error('rabbit: isConnected - false');
549
+ return false;
550
+ }
551
+ logger_1.default.info('rabbit: isConnected - true');
552
+ return true;
534
553
  }
535
554
  async gracefulShutdown(signal) {
536
555
  const tagsNumber = this.consumersTags.length;
@@ -3,6 +3,7 @@ export declare const DEFAULT_LOCK_TIMEOUT: number;
3
3
  export declare const RETRY_HEADER = "x-retry-count";
4
4
  export declare const TRACING_HEADER = "x-trace-id";
5
5
  export declare const USER_TRACING_HEADER = "x-af-user-id";
6
+ export declare const AUTOMATION_ID_HEADER = "x-af-automation-id";
6
7
  export declare const USER_OBJECT = "userObject";
7
8
  export declare const DEFAULT_USE_CONSUME_WITH_LOCK = false;
8
9
  export declare const CONNECTION_CREATED_CONST = "connectionCreated";
@@ -1,11 +1,12 @@
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.USER_TRACING_HEADER = exports.TRACING_HEADER = exports.RETRY_HEADER = exports.DEFAULT_LOCK_TIMEOUT = exports.DEFAULT_DEAD_TTL_TWO_DAYS = void 0;
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;
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';
7
7
  exports.TRACING_HEADER = 'x-trace-id';
8
8
  exports.USER_TRACING_HEADER = 'x-af-user-id';
9
+ exports.AUTOMATION_ID_HEADER = 'x-af-automation-id';
9
10
  exports.USER_OBJECT = 'userObject';
10
11
  exports.DEFAULT_USE_CONSUME_WITH_LOCK = false;
11
12
  exports.CONNECTION_CREATED_CONST = 'connectionCreated';
@@ -1,4 +1,3 @@
1
- import { AmqpConnectionManager } from 'amqp-connection-manager';
2
1
  import { ConsumeMessage, Options, Replies } from 'amqplib';
3
2
  export interface ExchangesCache {
4
3
  [key: string]: any;
@@ -9,6 +8,9 @@ export interface QueuesCache {
9
8
  export interface QueueSetupPromisesDictionary {
10
9
  [key: string]: Promise<Replies.AssertQueue> | undefined;
11
10
  }
11
+ export interface AssertExchangePromisesDictionary {
12
+ [key: string]: Promise<Replies.AssertExchange> | undefined;
13
+ }
12
14
  export type CustomMessageHeaders = {
13
15
  redisTimestampValidationKey?: string;
14
16
  };
@@ -39,11 +41,3 @@ export type AfConsumer = {
39
41
  options: ConsumeOptions | undefined;
40
42
  };
41
43
  export declare const CONSUMER_DEFAULT_OPTIONS: Options.Consume;
42
- export type ConnectionData = {
43
- connection: AmqpConnectionManager | null;
44
- creatingConnection: boolean;
45
- };
46
- export declare enum ConnectionPurpose {
47
- Consume = "consume",
48
- Publish = "publish"
49
- }
package/dist/lib/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConnectionPurpose = exports.CONSUMER_DEFAULT_OPTIONS = void 0;
3
+ exports.CONSUMER_DEFAULT_OPTIONS = void 0;
4
4
  const HA_PROMOTE_ON_FAILURE = 'ha-promote-on-failure';
5
5
  const HA_PROMOTE_ON_SHUTDOWN = 'ha-promote-on-shutdown';
6
6
  exports.CONSUMER_DEFAULT_OPTIONS = {
@@ -9,8 +9,3 @@ exports.CONSUMER_DEFAULT_OPTIONS = {
9
9
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
10
10
  },
11
11
  };
12
- var ConnectionPurpose;
13
- (function (ConnectionPurpose) {
14
- ConnectionPurpose["Consume"] = "consume";
15
- ConnectionPurpose["Publish"] = "publish";
16
- })(ConnectionPurpose = exports.ConnectionPurpose || (exports.ConnectionPurpose = {}));
@@ -1,5 +1,5 @@
1
1
  import { ChannelWrapper } from 'amqp-connection-manager';
2
- import { ConfirmChannel } from 'amqplib';
3
- export declare const assertExchangeFanout: (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => Promise<import("amqplib").Replies.AssertExchange>;
2
+ import { ConfirmChannel, Replies } from 'amqplib';
3
+ export declare const assertExchangeFanout: (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => Promise<Replies.AssertExchange>;
4
4
  export declare const wrapSetImmediate: (callback: () => any) => Promise<any>;
5
5
  export declare const rand: () => number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "3.2.22-beta.4",
3
+ "version": "3.2.22",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/index.ts CHANGED
@@ -19,6 +19,7 @@ import RabbitError from './lib/rabbitError';
19
19
  import getRedisInstance, { RedisConfig } from './lib/redis';
20
20
  import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
21
21
  import {
22
+ AUTOMATION_ID_HEADER,
22
23
  CONNECTION_CREATED_CONST,
23
24
  CONNECTION_FAILED_CONST,
24
25
  DEFAULT_LOCK_TIMEOUT,
@@ -35,15 +36,17 @@ import {
35
36
  CustomMessageHeaders,
36
37
  QueuesCache,
37
38
  RedisLockType,
38
- ExchangesCache, CONSUMER_DEFAULT_OPTIONS, QueueSetupPromisesDictionary,
39
- ConnectionPurpose,
40
- ConnectionData,
39
+ ExchangesCache,
40
+ CONSUMER_DEFAULT_OPTIONS,
41
+ QueueSetupPromisesDictionary,
42
+ AssertExchangePromisesDictionary,
41
43
  } from './lib/types';
42
44
 
43
45
  // const debug = nodeDebug('af-rabbitmq')
44
46
  const debug = logger.debug.bind(logger);
45
47
 
46
48
  const PUBLISH_TIMEOUT = 1000 * 10;
49
+
47
50
  export interface IAfRabbitMq {
48
51
  ack: any;
49
52
  nack: any;
@@ -83,13 +86,11 @@ type newChannelOpts = {
83
86
  name?: string;
84
87
  onClose?: null | ((args: any | null) => void);
85
88
  options?: CreateChannelOpts | undefined;
86
- connectionPurpose?: ConnectionPurpose,
87
89
  };
88
90
 
89
91
  type assertChannelOpts = {
90
92
  channelName?: string;
91
93
  force?: boolean;
92
- connectionPurpose?: ConnectionPurpose;
93
94
  }
94
95
 
95
96
  type AfConsumer = {
@@ -101,7 +102,7 @@ type AfConsumer = {
101
102
  const HEARTBEAT = '60';
102
103
 
103
104
  class RabbitMq implements IAfRabbitMq {
104
- static parseMsg(msg: any): any {
105
+ static parseMsg(msg: any) : any {
105
106
  let { content } = msg;
106
107
  content = content.toString();
107
108
 
@@ -146,21 +147,22 @@ class RabbitMq implements IAfRabbitMq {
146
147
 
147
148
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
148
149
 
149
- blockReconnect: boolean | null | undefined;
150
+ blockReconnect: boolean | null | undefined
150
151
 
151
- connectionsMap: {
152
- [ConnectionPurpose.Consume]: ConnectionData;
153
- [ConnectionPurpose.Publish]: ConnectionData;
154
- };
152
+ connection: AmqpConnectionManager | null | undefined
155
153
 
156
154
  em: EventEmitter;
157
155
 
156
+ creatingConnection: boolean;
157
+
158
158
  exchanges: ExchangesCache;
159
159
 
160
160
  queues: QueuesCache;
161
161
 
162
162
  queueSetupPromises: QueueSetupPromisesDictionary;
163
163
 
164
+ assertExchangePromises: AssertExchangePromisesDictionary;
165
+
164
166
  options: AfRabbitOptions | undefined;
165
167
 
166
168
  redisClient: any;
@@ -176,13 +178,12 @@ class RabbitMq implements IAfRabbitMq {
176
178
  this.em = new EventEmitter();
177
179
  this.channel = null;
178
180
  this.publishChannelSetupPromise = null;
179
- this.connectionsMap = {
180
- [ConnectionPurpose.Consume]: { connection: null, creatingConnection: false },
181
- [ConnectionPurpose.Publish]: { connection: null, creatingConnection: false },
182
- };
181
+ this.connection = null;
182
+ this.creatingConnection = false;
183
183
  this.exchanges = {};
184
184
  this.queues = {};
185
185
  this.queueSetupPromises = {};
186
+ this.assertExchangePromises = {};
186
187
  this.consumers = [];
187
188
  this.options = options;
188
189
  this.redisClient = redisConfig && getRedisInstance(redisConfig);
@@ -215,7 +216,7 @@ class RabbitMq implements IAfRabbitMq {
215
216
  return false;
216
217
  }
217
218
 
218
- public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage): Promise<any> => {
219
+ public ack = (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp = false, releaseLock = null) => async (userMsg: ConsumeMessage) : Promise<any> => {
219
220
  if (msg) {
220
221
  debug('rabbit acking message', { deliveryTag: msg.fields.deliveryTag });
221
222
  await channel.ack(msg);
@@ -241,8 +242,8 @@ class RabbitMq implements IAfRabbitMq {
241
242
  userMsg: ConsumeMessageOrNull,
242
243
  {
243
244
  skipRetry = false,
244
- }: NackOptions = {},
245
- ): Promise<any> => {
245
+ }: NackOptions = { },
246
+ ) : Promise<any> => {
246
247
  await this.unlockRedisIfNeeded(releaseLock);
247
248
  if (channel && msg) {
248
249
  if (
@@ -276,31 +277,30 @@ class RabbitMq implements IAfRabbitMq {
276
277
  }
277
278
  }
278
279
 
279
- async getConnection(connectionPurpose: ConnectionPurpose) {
280
- return new Promise<AmqpConnectionManager | undefined | null>(async (resolve, reject) => {
281
- const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
280
+ async getConnection() {
281
+ return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
282
282
  if (this.blockReconnect) {
283
283
  debug('rabbit: block reconnect');
284
284
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
285
285
  // @ts-ignore
286
286
  return resolve();
287
287
  }
288
- if (connection !== null) {
289
- if (this.options?.disableReconnect || connection?.isConnected()) {
288
+ if (this.connection !== null) {
289
+ if (this.options?.disableReconnect || this.connection?.isConnected()) {
290
290
  debug('rabbit: connection - is connected');
291
291
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
292
292
  // @ts-ignore
293
- return resolve(connection);
293
+ return resolve(this.connection);
294
294
  }
295
295
  debug('rabbit: connection - reconnecting');
296
296
  }
297
- if (creatingConnection) {
297
+ if (this.creatingConnection) {
298
298
  debug('rabbit: creating connection emi');
299
299
  this.em.once(CONNECTION_CREATED_CONST, resolve);
300
300
  this.em.once(CONNECTION_FAILED_CONST, reject);
301
301
  return;
302
302
  }
303
- this.connectionsMap[connectionPurpose].creatingConnection = true;
303
+ this.creatingConnection = true;
304
304
  let isResolved = false;
305
305
 
306
306
  // It is import to use it as a function and not as a variable
@@ -317,13 +317,12 @@ class RabbitMq implements IAfRabbitMq {
317
317
  };
318
318
 
319
319
  const defaultUrls = findServers();
320
- const newConnection: AmqpConnectionManager = await connect(defaultUrls, {
320
+ const connection: AmqpConnectionManager = await connect(defaultUrls, {
321
321
  findServers,
322
322
  });
323
323
 
324
- this.connectionsMap[connectionPurpose].connection = newConnection;
325
-
326
- newConnection.on('error', (err) => {
324
+ this.connection = connection;
325
+ this.connection.on('error', (err) => {
327
326
  logger.error('rabbit: connection error', { err });
328
327
  if (!isResolved) {
329
328
  isResolved = true;
@@ -332,7 +331,7 @@ class RabbitMq implements IAfRabbitMq {
332
331
  }
333
332
  });
334
333
 
335
- newConnection.on('connectFailed', (err) => {
334
+ this.connection.on('connectFailed', (err) => {
336
335
  this.consumersTags = [];
337
336
  logger.error('rabbit: connection connectFailed', { err });
338
337
  if (!isResolved) {
@@ -342,7 +341,8 @@ class RabbitMq implements IAfRabbitMq {
342
341
  }
343
342
  });
344
343
 
345
- newConnection.on('disconnect', ({ err }) => {
344
+ this.connection.on('disconnect', ({ err }) => {
345
+ // this.channel = null;
346
346
  this.consumersTags = [];
347
347
  debug('rabbit: connection closed');
348
348
  if (this.options?.disableReconnect) {
@@ -352,29 +352,25 @@ class RabbitMq implements IAfRabbitMq {
352
352
  logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
353
353
  }
354
354
  });
355
- newConnection.once('connect', async () => {
355
+
356
+ this.connection.once('connect', async () => {
356
357
  debug('rabbit: connection established');
357
- this.connectionsMap[connectionPurpose].creatingConnection = false;
358
- this.em.emit(CONNECTION_CREATED_CONST, newConnection);
358
+ this.creatingConnection = false;
359
+ this.em.emit(CONNECTION_CREATED_CONST, connection);
359
360
  isResolved = true;
360
- resolve(newConnection);
361
+ resolve(connection);
361
362
  });
362
363
  });
363
364
  }
364
365
 
365
- async getNewChannel({
366
- name = rand().toString(), onClose = null, options = {}, connectionPurpose = ConnectionPurpose.Consume,
367
- }: newChannelOpts): Promise<ChannelWrapper> {
368
- let connection!: AmqpConnectionManager | undefined | null;
366
+ async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
367
+ let connection!: AmqpConnectionManager;
369
368
  try {
370
- connection = await this.getConnection(connectionPurpose);
369
+ connection = await this.getConnection();
371
370
  } catch (e) {
372
371
  logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
373
372
  throw e;
374
373
  }
375
- if (!connection) {
376
- throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
377
- }
378
374
  const channel = connection.createChannel({ ...options });
379
375
  once(channel, 'close').then((args) => {
380
376
  logger.error(`rabbit: channel ${name} closed`);
@@ -390,7 +386,7 @@ class RabbitMq implements IAfRabbitMq {
390
386
  }
391
387
  }
392
388
 
393
- async assertChannel({ force = false, connectionPurpose = ConnectionPurpose.Consume }: assertChannelOpts): Promise<ChannelWrapper> {
389
+ async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
394
390
  if (!this.publishChannelSetupPromise) {
395
391
  this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
396
392
  if (this.channel && !force) {
@@ -398,7 +394,7 @@ class RabbitMq implements IAfRabbitMq {
398
394
  }
399
395
 
400
396
  try {
401
- const channel = await this.getNewChannel({ connectionPurpose });
397
+ const channel = await this.getNewChannel({});
402
398
  channel.on('error', (err) => {
403
399
  logger.error('rabbit: channel error', { err });
404
400
  });
@@ -412,71 +408,102 @@ class RabbitMq implements IAfRabbitMq {
412
408
  return this.publishChannelSetupPromise;
413
409
  }
414
410
 
415
- async assertExchange(exchangeName: string, options: any = { connectionPurpose: ConnectionPurpose.Consume }) {
416
- const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
411
+ async assertExchange(exchangeName: string, options?: any) {
412
+ const channel: ChannelWrapper = await this.assertChannel();
413
+
417
414
  if (this.exchanges[exchangeName]) {
415
+ delete this.assertExchangePromises[exchangeName];
418
416
  return this.exchanges[exchangeName];
419
417
  }
420
- const exchange = await assertExchangeFanout(channel, exchangeName);
421
- this.exchanges[exchangeName] = exchange;
422
- return exchange;
418
+
419
+ if (this.assertExchangePromises[exchangeName]) {
420
+ return this.assertExchangePromises[exchangeName];
421
+ }
422
+
423
+ this.assertExchangePromises[exchangeName] = assertExchangeFanout(channel, exchangeName);
424
+ this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
425
+ return this.exchanges[exchangeName];
423
426
  }
424
427
 
425
- async getQueueLength(queue: string, connectionPurpose: ConnectionPurpose = ConnectionPurpose.Consume): Promise<Replies.AssertQueue> {
428
+ async getQueueLength(queue: string) {
426
429
  RabbitMq.validateName('queue', queue);
427
- const { connection } = this.connectionsMap[connectionPurpose];
428
430
  const { channel } = this;
429
431
  if (!channel) {
430
432
  throw new Error('channel is not defined');
431
433
  }
432
- debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
434
+ debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
433
435
  return channel?.checkQueue(queue);
434
436
  }
435
437
 
436
- private async deleteQueue(queue: string, connectionPurpose: ConnectionPurpose) {
438
+ private async deleteQueue(queue: string) {
437
439
  RabbitMq.validateName('queue', queue);
438
- const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
440
+ const channel: ChannelWrapper = await this.assertChannel();
439
441
  logger.info('rabbit: deleting queue', { queue });
440
442
  const deleteQueueRes = await channel.deleteQueue(queue);
441
443
  debug('queue deleted', deleteQueueRes);
442
444
  return deleteQueueRes;
443
445
  }
444
446
 
445
- async bindQueue(queue: string, exchange: string, connectionPurpose: ConnectionPurpose) {
446
- const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
447
+ async bindQueue(queue: string, exchange: string) {
448
+ const channel: ChannelWrapper = await this.assertChannel();
447
449
  await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
448
450
  return channel.bindQueue(queue, exchange, '');
449
451
  }
450
452
 
451
- async setupQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
453
+ async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
452
454
  let queue: Replies.AssertQueue;
455
+ const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
456
+ const localeOptions = {
457
+ ...options,
458
+ durable: true,
459
+ arguments: {
460
+ ...options?.arguments,
461
+ 'x-consumer-timeout': 1000 * 60 * 60 * 24,
462
+ 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
463
+ },
464
+ };
453
465
  try {
454
- const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose });
466
+ const channel: ChannelWrapper = await this.assertChannel();
455
467
  debug('assertQueue->channel.addSetup', { queueName });
456
- await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
468
+ await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
457
469
  debug('assertQueue->channel.assertQueue', { queueName });
458
- queue = await channel.assertQueue(queueName, options);
470
+ queue = await channel.assertQueue(queueName, localeOptions);
459
471
  } catch (e) {
460
472
  logger.error('rabbit: assertQueue error', { queueName, options, error: e });
461
473
  if (!this.options?.dontRetryAssert) {
462
474
  debug('retrying assertQueue', { queueName });
463
- const channel = await this.assertChannel({ force: true, connectionPurpose });
464
- await this.deleteQueue(queueName, connectionPurpose);
475
+ const channel = await this.assertChannel({ force: true });
476
+ await this.deleteQueue(queueName);
465
477
 
466
478
  debug('retrying assertQueue->channel.addSetup', { queueName });
467
- await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, options));
479
+ await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
468
480
  debug('retrying assertQueue->channel.assertQueue', { queueName });
469
- queue = await channel.assertQueue(queueName, options);
481
+ queue = await channel.assertQueue(queueName, localeOptions);
470
482
  } else {
471
483
  throw e;
472
484
  }
473
485
  }
474
486
 
475
- this.queues[queueName] = queueName;
487
+ this.queues[queueName] = queue;
476
488
  return queue;
477
489
  }
478
490
 
479
- async assertQueue(queueName: string, connectionPurpose: ConnectionPurpose, options?: Options.AssertQueue) {
491
+ static shouldUseQuorum(queueName: string): boolean {
492
+ const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
493
+
494
+ if (envQuorumQueuesWhitelist === '*') {
495
+ return true;
496
+ }
497
+
498
+ if (envQuorumQueuesWhitelist) {
499
+ const whitelist = envQuorumQueuesWhitelist.split(',');
500
+ return whitelist.includes(queueName);
501
+ }
502
+
503
+ return false;
504
+ }
505
+
506
+ async assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any> {
480
507
  RabbitMq.validateName('queue', queueName);
481
508
  if (this.queues[queueName]) {
482
509
  delete this.queueSetupPromises[queueName];
@@ -487,12 +514,12 @@ class RabbitMq implements IAfRabbitMq {
487
514
  return this.queueSetupPromises[queueName];
488
515
  }
489
516
 
490
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
517
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
491
518
  return this.queueSetupPromises[queueName];
492
519
  }
493
520
 
494
521
  private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
495
- const isConsumerExist: boolean = this.consumers.some((consumer) => consumer.queue === queue);
522
+ const isConsumerExist :boolean = this.consumers.some((consumer) => consumer.queue === queue);
496
523
  if (!isConsumerExist) {
497
524
  logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
498
525
  this.consumers.push({
@@ -541,10 +568,10 @@ class RabbitMq implements IAfRabbitMq {
541
568
  }
542
569
  logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
543
570
  }
544
- const channel = await this.getNewChannel({ connectionPurpose: ConnectionPurpose.Consume });
571
+ const channel = await this.getNewChannel({});
545
572
  return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
546
- await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
547
- await confirmChannel.prefetch(limit, true);
573
+ const q = await this.assertQueue(queue, optionsWithDefaults);
574
+ await confirmChannel.prefetch(limit, false);
548
575
  const { consumerTag } = await confirmChannel.consume(
549
576
  queue,
550
577
  async (msg: ConsumeMessageOrNull) => {
@@ -554,6 +581,7 @@ class RabbitMq implements IAfRabbitMq {
554
581
 
555
582
  const traceId = msg.properties.headers[TRACING_HEADER];
556
583
  const userId = msg.properties.headers[USER_TRACING_HEADER];
584
+ const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
557
585
  const parsedMessage = RabbitMq.parseMsg(msg);
558
586
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
559
587
  const trace = newTrace(traceTypes.RABBIT);
@@ -579,7 +607,10 @@ class RabbitMq implements IAfRabbitMq {
579
607
  }
580
608
 
581
609
  if (auditContext) {
582
- await auditContext(queue);
610
+ await auditContext(queue, {
611
+ userId,
612
+ automationId,
613
+ });
583
614
  }
584
615
  const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
585
616
  if (!shouldConsume) {
@@ -627,13 +658,13 @@ class RabbitMq implements IAfRabbitMq {
627
658
  });
628
659
  }
629
660
 
630
- async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
661
+ async consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions) : Promise<any> {
631
662
  const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
632
663
  RabbitMq.validateName('exchange', exchange);
633
664
  RabbitMq.validateName('queue', queue);
634
665
  const { limit, deadMessageTtl } = optionsWithDefaults;
635
666
  await this.saveConsumer(queue, callback, options);
636
- const channel: ChannelWrapper = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
667
+ const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
637
668
 
638
669
  return channel.addSetup(async (c: ConfirmChannel) => {
639
670
  const assertExchange = await assertExchangeFanout(c, exchange);
@@ -651,11 +682,11 @@ class RabbitMq implements IAfRabbitMq {
651
682
  });
652
683
  }
653
684
 
654
- async publish(exchange: string, content: any, customHeaders?: any): Promise<boolean> {
685
+ async publish(exchange: string, content: any, customHeaders?: any) : Promise<boolean> {
655
686
  return wrapSetImmediate(async () => {
656
687
  RabbitMq.validateName('exchange', exchange);
657
- const channel: ChannelWrapper = await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
658
- await this.assertExchange(exchange, { connectionPurpose: ConnectionPurpose.Publish });
688
+ const channel: ChannelWrapper = await this.assertChannel();
689
+ await this.assertExchange(exchange);
659
690
  await channel.publish(exchange, '',
660
691
  Buffer.from(JSON.stringify(content)),
661
692
  RabbitMq.getPublishOptions(customHeaders));
@@ -669,7 +700,7 @@ class RabbitMq implements IAfRabbitMq {
669
700
  customHeaders?: any,
670
701
  ): Promise<boolean | undefined> {
671
702
  try {
672
- await this.assertChannel({ connectionPurpose: ConnectionPurpose.Publish });
703
+ await this.assertChannel();
673
704
  } catch (e) {
674
705
  logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
675
706
  throw e;
@@ -677,7 +708,7 @@ class RabbitMq implements IAfRabbitMq {
677
708
 
678
709
  try {
679
710
  RabbitMq.validateName('queue', queue);
680
- await this.assertQueue(queue, ConnectionPurpose.Publish, options);
711
+ await this.assertQueue(queue, options);
681
712
  } catch (e) {
682
713
  logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
683
714
  throw e;
@@ -690,41 +721,34 @@ class RabbitMq implements IAfRabbitMq {
690
721
  debug(`rabbit: sending to queue ${queue}`, { res });
691
722
  return res;
692
723
  } catch (e) {
693
- logger.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
724
+ const isConnected = await this.isConnected();
725
+ logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
694
726
  throw e;
695
727
  }
696
728
  }
697
729
 
698
- async isConnected(): Promise<boolean> {
699
- const isEachConnectionConnected = await Promise.all(
700
- Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
701
- const { connection } = connectionData;
702
- if (connectionPurpose === ConnectionPurpose.Publish && !connection) {
703
- return true; // The connection hasn't been initialized yet, as no messages have been sent through it
704
- }
705
- const isConnected = connection?.isConnected();
706
- if (!isConnected) {
707
- logger.error('rabbit: isConnected - false');
708
- return false;
709
- }
710
- const channel: any = await this.assertChannel({ connectionPurpose: connectionPurpose as ConnectionPurpose });
711
- try {
712
- await Promise.all([
713
- channel.waitForConnect(),
714
- ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
715
- ]);
716
- } catch (e) {
717
- logger.error('rabbit: isConnected - false');
718
- return false;
719
- }
720
- logger.info('rabbit: isConnected - true');
721
- return true;
722
- }),
723
- );
724
- return isEachConnectionConnected.every((isConnected) => isConnected === true);
730
+ async isConnected() : Promise<boolean> {
731
+ const connection = await this.getConnection();
732
+ const isConnected = connection.isConnected();
733
+ if (!isConnected) {
734
+ logger.error('rabbit: isConnected - false');
735
+ return false;
736
+ }
737
+ const channel: any = await this.assertChannel();
738
+ try {
739
+ await Promise.all([
740
+ channel.waitForConnect(),
741
+ ...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
742
+ ]);
743
+ } catch (e) {
744
+ logger.error('rabbit: isConnected - false');
745
+ return false;
746
+ }
747
+ logger.info('rabbit: isConnected - true');
748
+ return true;
725
749
  }
726
750
 
727
- async gracefulShutdown(signal: string): Promise<void> {
751
+ async gracefulShutdown(signal: string) : Promise<void> {
728
752
  const tagsNumber = this.consumersTags.length;
729
753
  logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
730
754
  const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
package/src/lib/consts.ts CHANGED
@@ -3,6 +3,7 @@ export const DEFAULT_LOCK_TIMEOUT = 1000 * 5;
3
3
  export const RETRY_HEADER = 'x-retry-count';
4
4
  export const TRACING_HEADER = 'x-trace-id';
5
5
  export const USER_TRACING_HEADER = 'x-af-user-id';
6
+ export const AUTOMATION_ID_HEADER = 'x-af-automation-id';
6
7
  export const USER_OBJECT = 'userObject';
7
8
  export const DEFAULT_USE_CONSUME_WITH_LOCK = false;
8
9
  export const CONNECTION_CREATED_CONST = 'connectionCreated';
package/src/lib/types.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { AmqpConnectionManager } from 'amqp-connection-manager';
2
1
  import { ConsumeMessage, Options, Replies } from 'amqplib';
3
2
 
4
3
  export interface ExchangesCache {
@@ -13,6 +12,10 @@ export interface QueueSetupPromisesDictionary {
13
12
  [key: string]: Promise<Replies.AssertQueue> | undefined
14
13
  }
15
14
 
15
+ export interface AssertExchangePromisesDictionary {
16
+ [key: string]: Promise<Replies.AssertExchange> | undefined
17
+ }
18
+
16
19
  export type CustomMessageHeaders = {
17
20
  redisTimestampValidationKey?: string;
18
21
  }
@@ -56,13 +59,3 @@ export const CONSUMER_DEFAULT_OPTIONS: Options.Consume = {
56
59
  [HA_PROMOTE_ON_SHUTDOWN]: 'always',
57
60
  },
58
61
  };
59
-
60
- export type ConnectionData = {
61
- connection: AmqpConnectionManager | null;
62
- creatingConnection: boolean;
63
- };
64
-
65
- export enum ConnectionPurpose {
66
- Consume = 'consume',
67
- Publish = 'publish',
68
- }
package/src/lib/utils.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import { ChannelWrapper } from 'amqp-connection-manager';
2
- import { ConfirmChannel } from 'amqplib';
2
+ import { ConfirmChannel, Replies } from 'amqplib';
3
+
4
+ export const assertExchangeFanout = async (
5
+ c: ChannelWrapper | ConfirmChannel,
6
+ exchangeName: string,
7
+ ): Promise<Replies.AssertExchange> => c.assertExchange(exchangeName, 'fanout');
3
8
 
4
- export const assertExchangeFanout = async (c: ChannelWrapper | ConfirmChannel, exchangeName: string) => c.assertExchange(exchangeName, 'fanout');
5
9
  export const wrapSetImmediate = (callback: () => any) => new Promise<any>((resolve, reject) => {
6
10
  setImmediate(async () => {
7
11
  try {