@autofleet/rabbit 3.3.0-beta.0 → 3.3.0-beta.1

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 } from './lib/types';
6
+ import { ConnectionPurpose } from './lib/consts';
7
+ import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary, ConnectionData } from './lib/types';
7
8
  export interface IAfRabbitMq {
8
9
  ack: any;
9
10
  nack: any;
@@ -32,17 +33,17 @@ export interface AfRabbitOptions {
32
33
  */
33
34
  dontRetryAssert?: boolean;
34
35
  rabbitHost?: string;
35
- serviceName: string;
36
- podIp?: string;
37
36
  }
38
37
  type newChannelOpts = {
39
38
  name?: string;
40
39
  onClose?: null | ((args: any | null) => void);
41
40
  options?: CreateChannelOpts | undefined;
41
+ connectionPurpose?: ConnectionPurpose;
42
42
  };
43
43
  type assertChannelOpts = {
44
44
  channelName?: string;
45
45
  force?: boolean;
46
+ connectionPurpose?: ConnectionPurpose;
46
47
  };
47
48
  declare class RabbitMq implements IAfRabbitMq {
48
49
  static parseMsg(msg: any): any;
@@ -59,15 +60,18 @@ declare class RabbitMq implements IAfRabbitMq {
59
60
  };
60
61
  DISCONNECT_MSG: string;
61
62
  RECONNECT_MSG: string;
62
- channel: ChannelWrapper | null;
63
+ publishChannel: ChannelWrapper | null;
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
- podId: string;
67
- creatingConnection: boolean;
68
71
  exchanges: ExchangesCache;
69
72
  queues: QueuesCache;
70
73
  queueSetupPromises: QueueSetupPromisesDictionary;
74
+ assertExchangePromises: AssertExchangePromisesDictionary;
71
75
  options: AfRabbitOptions | undefined;
72
76
  redisClient: any;
73
77
  redisLock?: RedisLockType;
@@ -78,14 +82,15 @@ 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
91
  bindQueue(queue: string, exchange: string): Promise<void>;
88
92
  setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
93
+ static shouldUseQuorum(queueName: string): boolean;
89
94
  assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
90
95
  private saveConsumer;
91
96
  consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
@@ -94,8 +99,9 @@ declare class RabbitMq implements IAfRabbitMq {
94
99
  private consumeFromRabbit;
95
100
  consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
96
101
  publish(exchange: string, content: any, customHeaders?: any): Promise<boolean>;
97
- sendToQueue(queue: string, content: any, options?: any, customHeaders?: any, isBlocking?: boolean): Promise<boolean | undefined>;
102
+ sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<boolean | undefined>;
98
103
  isConnected(): Promise<boolean>;
99
104
  gracefulShutdown(signal: string): Promise<void>;
100
105
  }
101
106
  export default RabbitMq;
107
+ export { sendCeleryTaskViaHttp, } from './lib/celery';
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"));
@@ -116,15 +117,28 @@ class RabbitMq {
116
117
  }
117
118
  };
118
119
  this.em = new events_1.EventEmitter();
119
- this.channel = null;
120
- this.connection = null;
121
- this.creatingConnection = false;
120
+ this.publishChannel = null;
121
+ this.publishChannelSetupPromise = null;
122
+ this.connectionsMap = {
123
+ [consts_1.ConnectionPurpose.Consume]: {
124
+ connection: null,
125
+ creatingConnection: false,
126
+ connectionCreatedEventName: 'consumeConnectionCreated',
127
+ connectionFailedEventName: 'consumeConnectionFailed',
128
+ },
129
+ [consts_1.ConnectionPurpose.Publish]: {
130
+ connection: null,
131
+ creatingConnection: false,
132
+ connectionCreatedEventName: 'publishConnectionCreated',
133
+ connectionFailedEventName: 'publishConnectionFailed',
134
+ },
135
+ };
122
136
  this.exchanges = {};
123
137
  this.queues = {};
124
138
  this.queueSetupPromises = {};
139
+ this.assertExchangePromises = {};
125
140
  this.consumers = [];
126
141
  this.options = options;
127
- this.podId = `${options?.serviceName}${options?.podIp ? `-${options.podIp}` : ''}`;
128
142
  this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
129
143
  if (this.redisClient) {
130
144
  this.redisLock = (0, util_1.promisify)((0, redis_lock_1.default)(this.redisClient));
@@ -140,30 +154,31 @@ class RabbitMq {
140
154
  });
141
155
  }
142
156
  }
143
- async getConnection() {
157
+ async getConnection(connectionPurpose) {
144
158
  return new Promise(async (resolve, reject) => {
159
+ const { connection, creatingConnection, connectionCreatedEventName, connectionFailedEventName, } = this.connectionsMap[connectionPurpose];
145
160
  if (this.blockReconnect) {
146
161
  debug('rabbit: block reconnect');
147
162
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
148
163
  // @ts-ignore
149
164
  return resolve();
150
165
  }
151
- if (this.connection !== null) {
152
- if (this.options?.disableReconnect || this.connection?.isConnected()) {
166
+ if (connection !== null) {
167
+ if (this.options?.disableReconnect || connection?.isConnected()) {
153
168
  debug('rabbit: connection - is connected');
154
169
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
155
170
  // @ts-ignore
156
- return resolve(this.connection);
171
+ return resolve(connection);
157
172
  }
158
173
  debug('rabbit: connection - reconnecting');
159
174
  }
160
- if (this.creatingConnection) {
175
+ if (creatingConnection) {
161
176
  debug('rabbit: creating connection emi');
162
- this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
163
- this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
177
+ this.em.once(connectionCreatedEventName, resolve);
178
+ this.em.once(connectionFailedEventName, reject);
164
179
  return;
165
180
  }
166
- this.creatingConnection = true;
181
+ this.connectionsMap[connectionPurpose].creatingConnection = true;
167
182
  let isResolved = false;
168
183
  // It is import to use it as a function and not as a variable
169
184
  // because of k8s changes the env variables
@@ -176,28 +191,29 @@ class RabbitMq {
176
191
  return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
177
192
  };
178
193
  const defaultUrls = findServers();
179
- const connection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
194
+ const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
180
195
  findServers,
181
196
  });
182
- this.connection = connection;
183
- this.connection.on('error', (err) => {
197
+ this.connectionsMap[connectionPurpose].connection = newConnection;
198
+ logger_1.default.info(`rabbit: created new connection ${connectionPurpose}`);
199
+ newConnection.on('error', (err) => {
184
200
  logger_1.default.error('rabbit: connection error', { err });
185
201
  if (!isResolved) {
186
202
  isResolved = true;
187
203
  reject(err);
188
- this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
204
+ this.em.emit(connectionFailedEventName, err);
189
205
  }
190
206
  });
191
- this.connection.on('connectFailed', (err) => {
207
+ newConnection.on('connectFailed', (err) => {
192
208
  this.consumersTags = [];
193
209
  logger_1.default.error('rabbit: connection connectFailed', { err });
194
210
  if (!isResolved) {
195
211
  isResolved = true;
196
212
  reject(err);
197
- this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
213
+ this.em.emit(connectionFailedEventName, err);
198
214
  }
199
215
  });
200
- this.connection.on('disconnect', ({ err }) => {
216
+ newConnection.on('disconnect', ({ err }) => {
201
217
  this.consumersTags = [];
202
218
  debug('rabbit: connection closed');
203
219
  if (this.options?.disableReconnect) {
@@ -208,25 +224,27 @@ class RabbitMq {
208
224
  logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
209
225
  }
210
226
  });
211
- this.connection.once('connect', async () => {
212
- debug('rabbit: connection established');
213
- this.creatingConnection = false;
214
- this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
227
+ newConnection.once('connect', async () => {
228
+ this.connectionsMap[connectionPurpose].creatingConnection = false;
229
+ this.em.emit(connectionCreatedEventName, newConnection);
215
230
  isResolved = true;
216
- resolve(connection);
231
+ resolve(newConnection);
217
232
  });
218
233
  });
219
234
  }
220
- async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {} } = {}) {
235
+ async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = consts_1.ConnectionPurpose.Consume, }) {
221
236
  let connection;
222
237
  try {
223
- connection = await this.getConnection();
238
+ connection = await this.getConnection(connectionPurpose);
224
239
  }
225
240
  catch (e) {
226
241
  logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
227
242
  throw e;
228
243
  }
229
- const channel = connection.createChannel({ ...options, name });
244
+ if (!connection) {
245
+ throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
246
+ }
247
+ const channel = connection.createChannel({ ...options });
230
248
  (0, events_1.once)(channel, 'close').then((args) => {
231
249
  logger_1.default.error(`rabbit: channel ${name} closed`);
232
250
  onClose?.(args);
@@ -241,83 +259,118 @@ class RabbitMq {
241
259
  throw err;
242
260
  }
243
261
  }
244
- async assertChannel({ force = false } = {}) {
245
- return new Promise(async (resolve, reject) => {
246
- if (this.channel && !force) {
247
- return resolve(this.channel);
248
- }
249
- try {
250
- const channel = await this.getNewChannel({});
251
- channel.on('error', (err) => {
252
- logger_1.default.error('rabbit: channel error', { err });
253
- });
254
- this.channel = channel;
255
- resolve(channel);
256
- }
257
- catch (e) {
258
- reject(e);
259
- }
260
- });
262
+ async assertChannel({ force = false, connectionPurpose = consts_1.ConnectionPurpose.Consume }) {
263
+ debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
264
+ if (!this.publishChannelSetupPromise) {
265
+ this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
266
+ if (this.publishChannel && !force) {
267
+ return resolve(this.publishChannel);
268
+ }
269
+ try {
270
+ const channel = await this.getNewChannel({ connectionPurpose });
271
+ debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
272
+ channel.on('error', (err) => {
273
+ logger_1.default.error('rabbit: channel error', { err });
274
+ });
275
+ if (connectionPurpose === consts_1.ConnectionPurpose.Publish) {
276
+ this.publishChannel = channel;
277
+ }
278
+ resolve(channel);
279
+ }
280
+ catch (e) {
281
+ reject(e);
282
+ }
283
+ });
284
+ }
285
+ return this.publishChannelSetupPromise;
261
286
  }
262
- async assertExchange(exchangeName, options) {
263
- const channel = await this.assertChannel();
287
+ async assertExchange(exchangeName, options = { connectionPurpose: consts_1.ConnectionPurpose.Consume }) {
288
+ const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
264
289
  if (this.exchanges[exchangeName]) {
290
+ delete this.assertExchangePromises[exchangeName];
265
291
  return this.exchanges[exchangeName];
266
292
  }
267
- const exchange = await (0, utils_1.assertExchangeFanout)(channel, exchangeName);
268
- this.exchanges[exchangeName] = exchange;
269
- return exchange;
293
+ if (this.assertExchangePromises[exchangeName]) {
294
+ return this.assertExchangePromises[exchangeName];
295
+ }
296
+ this.assertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
297
+ this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
298
+ return this.exchanges[exchangeName];
270
299
  }
271
- async getQueueLength(queue) {
300
+ async getQueueLength(queue, connectionPurpose = consts_1.ConnectionPurpose.Consume) {
272
301
  RabbitMq.validateName('queue', queue);
273
- const { channel } = this;
274
- if (!channel) {
302
+ const { connection } = this.connectionsMap[connectionPurpose];
303
+ const { publishChannel } = this;
304
+ if (!publishChannel) {
275
305
  throw new Error('channel is not defined');
276
306
  }
277
- debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
278
- return channel?.checkQueue(queue);
307
+ debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
308
+ return publishChannel?.checkQueue(queue);
279
309
  }
280
- async deleteQueue(queue) {
310
+ async deleteQueue(queue, connectionPurpose) {
281
311
  RabbitMq.validateName('queue', queue);
282
- const channel = await this.assertChannel();
312
+ const channel = await this.assertChannel({ connectionPurpose });
283
313
  logger_1.default.info('rabbit: deleting queue', { queue });
284
314
  const deleteQueueRes = await channel.deleteQueue(queue);
285
315
  debug('queue deleted', deleteQueueRes);
286
316
  return deleteQueueRes;
287
317
  }
288
318
  async bindQueue(queue, exchange) {
289
- const channel = await this.assertChannel();
319
+ const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
290
320
  await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
291
321
  return channel.bindQueue(queue, exchange, '');
292
322
  }
293
323
  async setupQueue(queueName, options) {
294
324
  let queue;
325
+ const connectionPurpose = consts_1.ConnectionPurpose.Publish;
326
+ const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
327
+ const localeOptions = {
328
+ ...options,
329
+ durable: true,
330
+ arguments: {
331
+ ...options?.arguments,
332
+ 'x-consumer-timeout': 1000 * 60 * 60 * 24,
333
+ 'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
334
+ },
335
+ };
295
336
  try {
296
- const channel = await this.assertChannel();
337
+ const channel = await this.assertChannel({ connectionPurpose });
297
338
  debug('assertQueue->channel.addSetup', { queueName });
298
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
339
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
299
340
  debug('assertQueue->channel.assertQueue', { queueName });
300
- queue = await channel.assertQueue(queueName, options);
341
+ queue = await channel.assertQueue(queueName, localeOptions);
301
342
  }
302
343
  catch (e) {
303
344
  logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
304
345
  if (!this.options?.dontRetryAssert) {
305
346
  debug('retrying assertQueue', { queueName });
306
- const channel = await this.assertChannel({ force: true });
307
- await this.deleteQueue(queueName);
347
+ const channel = await this.assertChannel({ force: true, connectionPurpose });
348
+ await this.deleteQueue(queueName, connectionPurpose);
308
349
  debug('retrying assertQueue->channel.addSetup', { queueName });
309
- await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, options));
350
+ await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
310
351
  debug('retrying assertQueue->channel.assertQueue', { queueName });
311
- queue = await channel.assertQueue(queueName, options);
352
+ queue = await channel.assertQueue(queueName, localeOptions);
312
353
  }
313
354
  else {
314
355
  throw e;
315
356
  }
316
357
  }
317
- this.queues[queueName] = queueName;
358
+ this.queues[queueName] = queue;
318
359
  return queue;
319
360
  }
361
+ static shouldUseQuorum(queueName) {
362
+ const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
363
+ if (envQuorumQueuesWhitelist === '*') {
364
+ return true;
365
+ }
366
+ if (envQuorumQueuesWhitelist) {
367
+ const whitelist = envQuorumQueuesWhitelist.split(',');
368
+ return whitelist.includes(queueName);
369
+ }
370
+ return false;
371
+ }
320
372
  async assertQueue(queueName, options) {
373
+ debug('rabbit: start assert queue', { queueName });
321
374
  RabbitMq.validateName('queue', queueName);
322
375
  if (this.queues[queueName]) {
323
376
  delete this.queueSetupPromises[queueName];
@@ -327,6 +380,7 @@ class RabbitMq {
327
380
  return this.queueSetupPromises[queueName];
328
381
  }
329
382
  this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
383
+ debug('rabbit: done assert queue', { queueName });
330
384
  return this.queueSetupPromises[queueName];
331
385
  }
332
386
  saveConsumer(queue, callback, options) {
@@ -369,16 +423,17 @@ class RabbitMq {
369
423
  }
370
424
  logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
371
425
  }
372
- const channel = await this.getNewChannel({ name: `${this.podId}_queue_${queue}` });
426
+ const channel = await this.getNewChannel({ connectionPurpose: consts_1.ConnectionPurpose.Consume });
373
427
  return channel.addSetup(async (confirmChannel) => {
374
- await confirmChannel.assertQueue(queue, options ? { messageTtl: options.messageTtl } : undefined);
375
- await confirmChannel.prefetch(limit, true);
428
+ const q = await this.assertQueue(queue, optionsWithDefaults);
429
+ await confirmChannel.prefetch(limit, false);
376
430
  const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
377
431
  if (!msg) {
378
432
  return null;
379
433
  }
380
434
  const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
381
435
  const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
436
+ const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
382
437
  const parsedMessage = RabbitMq.parseMsg(msg);
383
438
  const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
384
439
  const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
@@ -403,7 +458,10 @@ class RabbitMq {
403
458
  outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
404
459
  }
405
460
  if (auditContext) {
406
- await auditContext(queue);
461
+ await auditContext(queue, {
462
+ userId,
463
+ automationId,
464
+ });
407
465
  }
408
466
  const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
409
467
  if (!shouldConsume) {
@@ -449,12 +507,12 @@ class RabbitMq {
449
507
  RabbitMq.validateName('queue', queue);
450
508
  const { limit, deadMessageTtl } = optionsWithDefaults;
451
509
  await this.saveConsumer(queue, callback, options);
452
- const channel = await this.getNewChannel({ name: `${this.podId}_exchange_${exchange}_queue_${queue}` });
510
+ const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
453
511
  return channel.addSetup(async (c) => {
454
512
  const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
455
513
  await c.assertQueue(queue);
456
514
  this.exchanges[exchange] = assertExchange;
457
- await c.prefetch(limit, true);
515
+ await c.prefetch(limit, false);
458
516
  return Promise.all([
459
517
  c.bindQueue(queue, exchange, ''),
460
518
  this.consume(queue, callback, options),
@@ -462,53 +520,71 @@ class RabbitMq {
462
520
  });
463
521
  }
464
522
  async publish(exchange, content, customHeaders) {
523
+ debug('rabbit: start publish msg');
465
524
  return (0, utils_1.wrapSetImmediate)(async () => {
466
525
  RabbitMq.validateName('exchange', exchange);
467
- const channel = await this.assertChannel();
468
- await this.assertExchange(exchange);
526
+ const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
527
+ await this.assertExchange(exchange, { connectionPurpose: consts_1.ConnectionPurpose.Publish });
469
528
  await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
470
529
  });
471
530
  }
472
- async sendToQueue(queue, content, options, customHeaders, isBlocking) {
473
- const callback = async () => {
474
- try {
475
- RabbitMq.validateName('queue', queue);
476
- await this.assertChannel();
477
- await this.assertQueue(queue, options);
478
- const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
479
- debug(`rabbit: sending to queue ${queue}`, { res });
480
- return res;
481
- }
482
- catch (e) {
483
- logger_1.default.error(`rabbit: failed to send to queue ${queue}`, { e });
484
- throw e;
485
- }
486
- };
487
- if (isBlocking) {
488
- return callback();
531
+ async sendToQueue(queue, content, options, customHeaders) {
532
+ try {
533
+ await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
489
534
  }
490
- return (0, utils_1.wrapSetImmediate)(callback);
491
- }
492
- async isConnected() {
493
- const connection = await this.getConnection();
494
- const isConnected = connection.isConnected();
495
- if (!isConnected) {
496
- logger_1.default.error('rabbit: isConnected - false');
497
- return false;
535
+ catch (e) {
536
+ logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
537
+ throw e;
498
538
  }
499
- const channel = await this.assertChannel();
500
539
  try {
501
- await Promise.all([
502
- channel.waitForConnect(),
503
- ...this.consumers.map((c) => channel.checkQueue(c.queue)),
504
- ]);
540
+ RabbitMq.validateName('queue', queue);
541
+ await this.assertQueue(queue, options);
505
542
  }
506
543
  catch (e) {
507
- logger_1.default.error('rabbit: isConnected - false');
508
- return false;
544
+ logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
545
+ throw e;
546
+ }
547
+ try {
548
+ const res = await this.publishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
549
+ debug(`rabbit: sending to queue ${queue}`, { res });
550
+ return res;
551
+ }
552
+ catch (e) {
553
+ logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
554
+ throw e;
509
555
  }
510
- logger_1.default.info('rabbit: isConnected - true');
511
- return true;
556
+ }
557
+ async isConnected() {
558
+ debug('rabbit: start is connected');
559
+ const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
560
+ const { connection } = connectionData;
561
+ if (connection) {
562
+ const isConnected = connection.isConnected();
563
+ if (!isConnected) {
564
+ logger_1.default.error('rabbit: isConnected - false', { connectionPurpose });
565
+ return false;
566
+ }
567
+ logger_1.default.info('rabbit: isConnected - true', { connectionPurpose });
568
+ if (connectionPurpose === consts_1.ConnectionPurpose.Publish) {
569
+ const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
570
+ try {
571
+ await Promise.all([
572
+ channel.waitForConnect(),
573
+ ...this.consumers.map((c) => channel.checkQueue(c.queue)),
574
+ ]);
575
+ }
576
+ catch (e) {
577
+ logger_1.default.error('rabbit: isConnected - false');
578
+ return false;
579
+ }
580
+ }
581
+ }
582
+ else {
583
+ logger_1.default.info('rabbit: connection hasnt initialized yet', { connectionPurpose });
584
+ }
585
+ return true;
586
+ }));
587
+ return isEachConnectionConnected.every((isConnected) => isConnected === true);
512
588
  }
513
589
  async gracefulShutdown(signal) {
514
590
  const tagsNumber = this.consumersTags.length;
@@ -527,3 +603,5 @@ class RabbitMq {
527
603
  }
528
604
  }
529
605
  exports.default = RabbitMq;
606
+ var celery_1 = require("./lib/celery");
607
+ Object.defineProperty(exports, "sendCeleryTaskViaHttp", { enumerable: true, get: function () { return celery_1.sendCeleryTaskViaHttp; } });
@@ -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,10 +3,9 @@ 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
- export declare const CONNECTION_CREATED_CONST = "connectionCreated";
9
- export declare const CONNECTION_FAILED_CONST = "connectionFailed";
10
9
  export declare const DEFAULT_OPTIONS: {
11
10
  limit: number;
12
11
  retries: number;
@@ -16,3 +15,7 @@ export declare const DEFAULT_OPTIONS: {
16
15
  auditContext: null;
17
16
  enableRabbitTrace: boolean;
18
17
  };
18
+ export declare enum ConnectionPurpose {
19
+ Consume = "consume",
20
+ Publish = "publish"
21
+ }