@autofleet/rabbit 3.2.22-beta.5 → 3.2.23-beta.0.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,7 +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 { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, ConnectionPurpose, ConnectionData } from './lib/types';
6
+ import { sendCeleryTaskViaHttp } from './lib/celery';
7
+ import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary } from './lib/types';
7
8
  export interface IAfRabbitMq {
8
9
  ack: any;
9
10
  nack: any;
@@ -37,12 +38,10 @@ type newChannelOpts = {
37
38
  name?: string;
38
39
  onClose?: null | ((args: any | null) => void);
39
40
  options?: CreateChannelOpts | undefined;
40
- connectionPurpose?: ConnectionPurpose;
41
41
  };
42
42
  type assertChannelOpts = {
43
43
  channelName?: string;
44
44
  force?: boolean;
45
- connectionPurpose?: ConnectionPurpose;
46
45
  };
47
46
  declare class RabbitMq implements IAfRabbitMq {
48
47
  static parseMsg(msg: any): any;
@@ -62,14 +61,13 @@ declare class RabbitMq implements IAfRabbitMq {
62
61
  channel: ChannelWrapper | null;
63
62
  publishChannelSetupPromise: Promise<ChannelWrapper> | null;
64
63
  blockReconnect: boolean | null | undefined;
65
- connectionsMap: {
66
- [ConnectionPurpose.Consume]: ConnectionData;
67
- [ConnectionPurpose.Publish]: ConnectionData;
68
- };
64
+ connection: AmqpConnectionManager | null | undefined;
69
65
  em: EventEmitter;
66
+ creatingConnection: boolean;
70
67
  exchanges: ExchangesCache;
71
68
  queues: QueuesCache;
72
69
  queueSetupPromises: QueueSetupPromisesDictionary;
70
+ assertExchangePromises: AssertExchangePromisesDictionary;
73
71
  options: AfRabbitOptions | undefined;
74
72
  redisClient: any;
75
73
  redisLock?: RedisLockType;
@@ -80,15 +78,16 @@ declare class RabbitMq implements IAfRabbitMq {
80
78
  private shouldConsumeMessageByTimestamp;
81
79
  ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
82
80
  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>;
81
+ getConnection(): Promise<AmqpConnectionManager>;
82
+ getNewChannel({ name, onClose, options }?: newChannelOpts): Promise<ChannelWrapper>;
83
+ assertChannel({ force }?: assertChannelOpts): Promise<ChannelWrapper>;
86
84
  assertExchange(exchangeName: string, options?: any): Promise<any>;
87
- getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
85
+ getQueueLength(queue: string): Promise<Replies.AssertQueue>;
88
86
  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>;
87
+ bindQueue(queue: string, exchange: string): Promise<void>;
88
+ setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
89
+ static shouldUseQuorum(queueName: string): boolean;
90
+ assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
92
91
  private saveConsumer;
93
92
  consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
94
93
  private lockRedisIfNeeded;
@@ -101,3 +100,4 @@ declare class RabbitMq implements IAfRabbitMq {
101
100
  gracefulShutdown(signal: string): Promise<void>;
102
101
  }
103
102
  export default RabbitMq;
103
+ export { sendCeleryTaskViaHttp, };
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.sendCeleryTaskViaHttp = void 0;
7
8
  const events_1 = require("events");
8
9
  const util_1 = require("util");
9
10
  const moment_1 = __importDefault(require("moment"));
@@ -15,6 +16,8 @@ const logger_1 = __importDefault(require("./logger"));
15
16
  const rabbitError_1 = __importDefault(require("./lib/rabbitError"));
16
17
  const redis_1 = __importDefault(require("./lib/redis"));
17
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; } });
18
21
  const consts_1 = require("./lib/consts");
19
22
  const types_1 = require("./lib/types");
20
23
  // const debug = nodeDebug('af-rabbitmq')
@@ -118,13 +121,12 @@ class RabbitMq {
118
121
  this.em = new events_1.EventEmitter();
119
122
  this.channel = null;
120
123
  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
- };
124
+ this.connection = null;
125
+ this.creatingConnection = false;
125
126
  this.exchanges = {};
126
127
  this.queues = {};
127
128
  this.queueSetupPromises = {};
129
+ this.assertExchangePromises = {};
128
130
  this.consumers = [];
129
131
  this.options = options;
130
132
  this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
@@ -142,32 +144,30 @@ class RabbitMq {
142
144
  });
143
145
  }
144
146
  }
145
- async getConnection(connectionPurpose) {
147
+ async getConnection() {
146
148
  return new Promise(async (resolve, reject) => {
147
- const { connection, creatingConnection } = this.connectionsMap[connectionPurpose];
148
- debug('rabbit: start getting new connection', { connection, creatingConnection, connectionPurpose });
149
149
  if (this.blockReconnect) {
150
150
  debug('rabbit: block reconnect');
151
151
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
152
152
  // @ts-ignore
153
153
  return resolve();
154
154
  }
155
- if (connection !== null) {
156
- if (this.options?.disableReconnect || connection?.isConnected()) {
155
+ if (this.connection !== null) {
156
+ if (this.options?.disableReconnect || this.connection?.isConnected()) {
157
157
  debug('rabbit: connection - is connected');
158
158
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
159
159
  // @ts-ignore
160
- return resolve(connection);
160
+ return resolve(this.connection);
161
161
  }
162
162
  debug('rabbit: connection - reconnecting');
163
163
  }
164
- if (creatingConnection) {
164
+ if (this.creatingConnection) {
165
165
  debug('rabbit: creating connection emi');
166
166
  this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
167
167
  this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
168
168
  return;
169
169
  }
170
- this.connectionsMap[connectionPurpose].creatingConnection = true;
170
+ this.creatingConnection = true;
171
171
  let isResolved = false;
172
172
  // It is import to use it as a function and not as a variable
173
173
  // because of k8s changes the env variables
@@ -180,11 +180,11 @@ class RabbitMq {
180
180
  return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
181
181
  };
182
182
  const defaultUrls = findServers();
183
- const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
183
+ const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
184
184
  findServers,
185
185
  });
186
- this.connectionsMap[connectionPurpose].connection = newConnection;
187
- newConnection.on('error', (err) => {
186
+ this.connection = connection;
187
+ this.connection.on('error', (err) => {
188
188
  logger_1.default.error('rabbit: connection error', { err });
189
189
  if (!isResolved) {
190
190
  isResolved = true;
@@ -192,7 +192,7 @@ class RabbitMq {
192
192
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
193
193
  }
194
194
  });
195
- newConnection.on('connectFailed', (err) => {
195
+ this.connection.on('connectFailed', (err) => {
196
196
  this.consumersTags = [];
197
197
  logger_1.default.error('rabbit: connection connectFailed', { err });
198
198
  if (!isResolved) {
@@ -201,7 +201,8 @@ class RabbitMq {
201
201
  this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
202
202
  }
203
203
  });
204
- newConnection.on('disconnect', ({ err }) => {
204
+ this.connection.on('disconnect', ({ err }) => {
205
+ // this.channel = null;
205
206
  this.consumersTags = [];
206
207
  debug('rabbit: connection closed');
207
208
  if (this.options?.disableReconnect) {
@@ -212,27 +213,24 @@ class RabbitMq {
212
213
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
213
214
  }
214
215
  });
215
- newConnection.once('connect', async () => {
216
- debug('rabbit: connection established', { connectionPurpose, connection: this.connectionsMap[connectionPurpose] });
217
- this.connectionsMap[connectionPurpose].creatingConnection = false;
218
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, newConnection);
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);
219
220
  isResolved = true;
220
- resolve(newConnection);
221
+ resolve(connection);
221
222
  });
222
223
  });
223
224
  }
224
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = types_1.ConnectionPurpose.Consume, }) {
225
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
225
226
  let connection;
226
227
  try {
227
- connection = await this.getConnection(connectionPurpose);
228
+ connection = await this.getConnection();
228
229
  }
229
230
  catch (e) {
230
231
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
231
232
  throw e;
232
233
  }
233
- if (!connection) {
234
- throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
235
- }
236
234
  const channel = connection.createChannel({ ...options });
237
235
  (0, events_1.once)(channel, 'close').then((args) => {
238
236
  logger_1.default.error(`rabbit: channel ${name} closed`);
@@ -248,16 +246,14 @@ class RabbitMq {
248
246
  throw err;
249
247
  }
250
248
  }
251
- async assertChannel({ force = false, connectionPurpose = types_1.ConnectionPurpose.Consume }) {
252
- debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.channel });
249
+ async assertChannel({ force = false } = {}) {
253
250
  if (!this.publishChannelSetupPromise) {
254
251
  this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
255
252
  if (this.channel && !force) {
256
253
  return resolve(this.channel);
257
254
  }
258
255
  try {
259
- const channel = await this.getNewChannel({ connectionPurpose });
260
- debug('rabbit: new channel got', { connectionPurpose, channel: this.channel });
256
+ const channel = await this.getNewChannel({});
261
257
  channel.on('error', (err) => {
262
258
  logger_1.default.error('rabbit: channel error', { err });
263
259
  });
@@ -271,69 +267,90 @@ class RabbitMq {
271
267
  }
272
268
  return this.publishChannelSetupPromise;
273
269
  }
274
- async assertExchange(exchangeName, options = { connectionPurpose: types_1.ConnectionPurpose.Consume }) {
275
- const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
270
+ async assertExchange(exchangeName, options) {
271
+ const channel = await this.assertChannel();
276
272
  if (this.exchanges[exchangeName]) {
273
+ delete this.assertExchangePromises[exchangeName];
277
274
  return this.exchanges[exchangeName];
278
275
  }
279
- const exchange = await (0, utils_1.assertExchangeFanout)(channel, exchangeName);
280
- this.exchanges[exchangeName] = exchange;
281
- return exchange;
276
+ if (this.assertExchangePromises[exchangeName]) {
277
+ return this.assertExchangePromises[exchangeName];
278
+ }
279
+ this.assertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
280
+ this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
281
+ return this.exchanges[exchangeName];
282
282
  }
283
- async getQueueLength(queue, connectionPurpose = types_1.ConnectionPurpose.Consume) {
283
+ async getQueueLength(queue) {
284
284
  RabbitMq.validateName('queue', queue);
285
- const { connection } = this.connectionsMap[connectionPurpose];
286
285
  const { channel } = this;
287
286
  if (!channel) {
288
287
  throw new Error('channel is not defined');
289
288
  }
290
- debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
289
+ debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
291
290
  return channel?.checkQueue(queue);
292
291
  }
293
- async deleteQueue(queue, connectionPurpose) {
292
+ async deleteQueue(queue) {
294
293
  RabbitMq.validateName('queue', queue);
295
- const channel = await this.assertChannel({ connectionPurpose });
294
+ const channel = await this.assertChannel();
296
295
  logger_1.default.info('rabbit: deleting queue', { queue });
297
296
  const deleteQueueRes = await channel.deleteQueue(queue);
298
297
  debug('queue deleted', deleteQueueRes);
299
298
  return deleteQueueRes;
300
299
  }
301
- async bindQueue(queue, exchange, connectionPurpose) {
302
- const channel = await this.assertChannel({ connectionPurpose });
300
+ async bindQueue(queue, exchange) {
301
+ const channel = await this.assertChannel();
303
302
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
304
303
  return channel.bindQueue(queue, exchange, '');
305
304
  }
306
- async setupQueue(queueName, connectionPurpose, options) {
305
+ async setupQueue(queueName, options) {
307
306
  let queue;
308
- debug('rabbit: start setup queue', { queueName, connectionPurpose });
307
+ const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
308
+ const localeOptions = {
309
+ ...options,
310
+ durable: true,
311
+ arguments: {
312
+ ...options?.arguments,
313
+ 'x-consumer-timeout': 1000 * 60 * 60 * 24,
314
+ 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
315
+ },
316
+ };
309
317
  try {
310
- const channel = await this.assertChannel({ connectionPurpose });
318
+ const channel = await this.assertChannel();
311
319
  debug('assertQueue->channel.addSetup', { queueName });
312
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
320
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
313
321
  debug('assertQueue->channel.assertQueue', { queueName });
314
- queue = await channel.assertQueue(queueName, options);
322
+ queue = await channel.assertQueue(queueName, localeOptions);
315
323
  }
316
324
  catch (e) {
317
325
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
318
326
  if (!this.options?.dontRetryAssert) {
319
327
  debug('retrying assertQueue', { queueName });
320
- const channel = await this.assertChannel({ force: true, connectionPurpose });
321
- await this.deleteQueue(queueName, connectionPurpose);
328
+ const channel = await this.assertChannel({ force: true });
329
+ await this.deleteQueue(queueName);
322
330
  debug('retrying assertQueue->channel.addSetup', { queueName });
323
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
331
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
324
332
  debug('retrying assertQueue->channel.assertQueue', { queueName });
325
- queue = await channel.assertQueue(queueName, options);
333
+ queue = await channel.assertQueue(queueName, localeOptions);
326
334
  }
327
335
  else {
328
336
  throw e;
329
337
  }
330
338
  }
331
- debug('rabbit: done setup queue', { queueName, connectionPurpose });
332
- this.queues[queueName] = queueName;
339
+ this.queues[queueName] = queue;
333
340
  return queue;
334
341
  }
335
- async assertQueue(queueName, connectionPurpose, options) {
336
- debug('rabbit: start assert queue', { connectionPurpose, queueName });
342
+ static shouldUseQuorum(queueName) {
343
+ const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
344
+ if (envQuorumQueuesWhitelist === '*') {
345
+ return true;
346
+ }
347
+ if (envQuorumQueuesWhitelist) {
348
+ const whitelist = envQuorumQueuesWhitelist.split(',');
349
+ return whitelist.includes(queueName);
350
+ }
351
+ return false;
352
+ }
353
+ async assertQueue(queueName, options) {
337
354
  RabbitMq.validateName('queue', queueName);
338
355
  if (this.queues[queueName]) {
339
356
  delete this.queueSetupPromises[queueName];
@@ -342,8 +359,7 @@ class RabbitMq {
342
359
  if (this.queueSetupPromises[queueName]) {
343
360
  return this.queueSetupPromises[queueName];
344
361
  }
345
- this.queueSetupPromises[queueName] = this.setupQueue(queueName, connectionPurpose, options);
346
- debug('rabbit: done assert queue', { connectionPurpose, queueName });
362
+ this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
347
363
  return this.queueSetupPromises[queueName];
348
364
  }
349
365
  saveConsumer(queue, callback, options) {
@@ -386,16 +402,17 @@ class RabbitMq {
386
402
  }
387
403
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
388
404
  }
389
- const channel = await this.getNewChannel({ connectionPurpose: types_1.ConnectionPurpose.Consume });
405
+ const channel = await this.getNewChannel({});
390
406
  return channel.addSetup(async (confirmChannel) => {
391
- await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
392
- await confirmChannel.prefetch(limit, true);
407
+ const q = await this.assertQueue(queue, optionsWithDefaults);
408
+ await confirmChannel.prefetch(limit, false);
393
409
  const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
394
410
  if (!msg) {
395
411
  return null;
396
412
  }
397
413
  const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
398
414
  const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
415
+ const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
399
416
  const parsedMessage = RabbitMq.parseMsg(msg);
400
417
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
401
418
  const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
@@ -420,7 +437,10 @@ class RabbitMq {
420
437
  outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
421
438
  }
422
439
  if (auditContext) {
423
- await auditContext(queue);
440
+ await auditContext(queue, {
441
+ userId,
442
+ automationId,
443
+ });
424
444
  }
425
445
  const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
426
446
  if (!shouldConsume) {
@@ -466,7 +486,7 @@ class RabbitMq {
466
486
  RabbitMq.validateName('queue', queue);
467
487
  const { limit, deadMessageTtl } = optionsWithDefaults;
468
488
  await this.saveConsumer(queue, callback, options);
469
- const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
489
+ const channel = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
470
490
  return channel.addSetup(async (c) => {
471
491
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
472
492
  await c.assertQueue(queue);
@@ -481,14 +501,14 @@ class RabbitMq {
481
501
  async publish(exchange, content, customHeaders) {
482
502
  return (0, utils_1.wrapSetImmediate)(async () => {
483
503
  RabbitMq.validateName('exchange', exchange);
484
- const channel = await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
485
- await this.assertExchange(exchange, { connectionPurpose: types_1.ConnectionPurpose.Publish });
504
+ const channel = await this.assertChannel();
505
+ await this.assertExchange(exchange);
486
506
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
487
507
  });
488
508
  }
489
509
  async sendToQueue(queue, content, options, customHeaders) {
490
510
  try {
491
- await this.assertChannel({ connectionPurpose: types_1.ConnectionPurpose.Publish });
511
+ await this.assertChannel();
492
512
  }
493
513
  catch (e) {
494
514
  logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
@@ -496,7 +516,7 @@ class RabbitMq {
496
516
  }
497
517
  try {
498
518
  RabbitMq.validateName('queue', queue);
499
- await this.assertQueue(queue, types_1.ConnectionPurpose.Publish, options);
519
+ await this.assertQueue(queue, options);
500
520
  }
501
521
  catch (e) {
502
522
  logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
@@ -508,36 +528,31 @@ class RabbitMq {
508
528
  return res;
509
529
  }
510
530
  catch (e) {
511
- logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
531
+ const isConnected = await this.isConnected();
532
+ logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
512
533
  throw e;
513
534
  }
514
535
  }
515
536
  async isConnected() {
516
- const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
517
- const { connection } = connectionData;
518
- if (connectionPurpose === types_1.ConnectionPurpose.Publish && !connection) {
519
- return true; // The connection hasn't been initialized yet, as no messages have been sent through it
520
- }
521
- const isConnected = connection?.isConnected();
522
- if (!isConnected) {
523
- logger_1.default.error('rabbit: isConnected - false');
524
- return false;
525
- }
526
- const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
527
- try {
528
- await Promise.all([
529
- channel.waitForConnect(),
530
- ...this.consumers.map((c) => channel.checkQueue(c.queue)),
531
- ]);
532
- }
533
- catch (e) {
534
- logger_1.default.error('rabbit: isConnected - false');
535
- return false;
536
- }
537
- logger_1.default.info('rabbit: isConnected - true');
538
- return true;
539
- }));
540
- return isEachConnectionConnected.every((isConnected) => isConnected === true);
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;
541
556
  }
542
557
  async gracefulShutdown(signal) {
543
558
  const tagsNumber = this.consumersTags.length;
@@ -0,0 +1,9 @@
1
+ interface TaskData {
2
+ [key: string]: any;
3
+ }
4
+ interface SendTaskOptions {
5
+ taskName: string;
6
+ queueName: string;
7
+ }
8
+ declare function sendCeleryTaskViaHttp(data: TaskData, { taskName, queueName }: SendTaskOptions): Promise<void>;
9
+ export { sendCeleryTaskViaHttp, TaskData, SendTaskOptions };
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.sendCeleryTaskViaHttp = void 0;
7
+ const node_crypto_1 = require("node:crypto");
8
+ const logger_1 = __importDefault(require("../logger"));
9
+ // Environment configuration
10
+ const config = {
11
+ host: process.env.RABBITMQ_SERVICE_HOST || 'localhost',
12
+ username: process.env.RABBITMQ_USERNAME || 'guest',
13
+ password: process.env.RABBITMQ_PASSWORD || 'guest',
14
+ };
15
+ async function sendCeleryTaskViaHttp(data, { taskName, queueName }) {
16
+ const apiUrl = `http://${config.host}:15672/api/exchanges/%2f/amq.default/publish`;
17
+ const message = {
18
+ task: taskName,
19
+ id: (0, node_crypto_1.randomUUID)(),
20
+ args: [data],
21
+ };
22
+ const payload = {
23
+ properties: {
24
+ delivery_mode: 2,
25
+ content_type: 'application/json',
26
+ },
27
+ routing_key: queueName,
28
+ payload: JSON.stringify(message),
29
+ payload_encoding: 'string',
30
+ };
31
+ try {
32
+ const response = await fetch(apiUrl, {
33
+ method: 'POST',
34
+ headers: {
35
+ 'Content-Type': 'application/json',
36
+ Authorization: `Basic ${Buffer.from(`${config.username}:${config.password}`).toString('base64')}`,
37
+ },
38
+ body: JSON.stringify(payload),
39
+ });
40
+ if (response.ok) {
41
+ const result = await response.json();
42
+ logger_1.default.info('Successfully published message:', result);
43
+ }
44
+ else {
45
+ logger_1.default.error(`Failed to publish message. Status code: ${response.status}`);
46
+ logger_1.default.error(`Response: ${await response.text()}`);
47
+ }
48
+ }
49
+ catch (error) {
50
+ logger_1.default.error('Error sending request:', error instanceof Error ? error.message : String(error));
51
+ throw error;
52
+ }
53
+ }
54
+ exports.sendCeleryTaskViaHttp = sendCeleryTaskViaHttp;
@@ -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,8 +1,11 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "3.2.22-beta.5",
3
+ "version": "3.2.23-beta.0.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
+ "engines": {
7
+ "node": ">=15.0.0"
8
+ },
6
9
  "scripts": {
7
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'",
8
11
  "start": "ts-node src/index.ts",
@@ -43,4 +46,4 @@
43
46
  },
44
47
  "author": "",
45
48
  "license": "ISC"
46
- }
49
+ }