@autofleet/rabbit 3.1.1 → 3.2.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
@@ -29,6 +29,7 @@ export interface AfRabbitOptions {
29
29
  * @default false
30
30
  */
31
31
  dontRetryAssert?: boolean;
32
+ rabbitHost?: string;
32
33
  }
33
34
  declare type newChannelOpts = {
34
35
  name?: string;
@@ -43,6 +44,7 @@ declare class RabbitMq implements IAfRabbitMq {
43
44
  static validateName(type: string, name: string): void;
44
45
  static getPublishOptions(customHeaders?: CustomMessageHeaders): {
45
46
  timestamp: number;
47
+ timeout: number;
46
48
  headers: {
47
49
  "x-af-user-id": any;
48
50
  "x-trace-id": any;
@@ -62,6 +64,9 @@ declare class RabbitMq implements IAfRabbitMq {
62
64
  options: AfRabbitOptions | undefined;
63
65
  redisClient: any;
64
66
  redisLock?: RedisLockType;
67
+ userName: string;
68
+ password: string;
69
+ host: string;
65
70
  /** Array of consumers tags used for canceling consumption */
66
71
  consumersTags: Array<[ConfirmChannel, string]>;
67
72
  private consumers;
package/dist/index.js CHANGED
@@ -18,14 +18,10 @@ const consts_1 = require("./lib/consts");
18
18
  const types_1 = require("./lib/types");
19
19
  // const debug = nodeDebug('af-rabbitmq')
20
20
  const debug = logger_1.default.info;
21
- const USERNAME = process.env.RABBITMQ_USERNAME || 'guest';
22
- const PASSWORD = process.env.RABBITMQ_PASSWORD || 'guest';
23
- const HOST = process.env.RABBITMQ_SERVICE_HOST || 'localhost';
21
+ const PUBLISH_TIMEOUT = 1000 * 10;
24
22
  const HEARTBEAT = '60';
25
23
  class RabbitMq {
26
24
  constructor(options, redisConfig) {
27
- // PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
28
- // PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
29
25
  this.DISCONNECT_MSG = 'rabbit: connection disconnect';
30
26
  this.RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
31
27
  this.consumers = [];
@@ -84,6 +80,9 @@ class RabbitMq {
84
80
  });
85
81
  }
86
82
  };
83
+ this.userName = process.env.RABBITMQ_USERNAME || 'guest';
84
+ this.password = process.env.RABBITMQ_PASSWORD || 'guest';
85
+ this.host = options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
87
86
  this.em = new events_1.EventEmitter();
88
87
  this.channel = null;
89
88
  this.connection = null;
@@ -130,6 +129,7 @@ class RabbitMq {
130
129
  const outbreakTrace = zehut_1.outbreak.getCurrentContext();
131
130
  return {
132
131
  timestamp: moment_1.default().unix(),
132
+ timeout: PUBLISH_TIMEOUT,
133
133
  headers: {
134
134
  creationTimestamp: moment_1.default().valueOf(),
135
135
  ...customHeaders,
@@ -139,7 +139,7 @@ class RabbitMq {
139
139
  };
140
140
  }
141
141
  async getConnection() {
142
- return new Promise(async (resolve) => {
142
+ return new Promise(async (resolve, reject) => {
143
143
  if (this.blockReconnect) {
144
144
  debug('rabbit: block reconnect');
145
145
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -158,18 +158,34 @@ class RabbitMq {
158
158
  if (this.creatingConnection) {
159
159
  debug('rabbit: creating connection emi');
160
160
  this.em.once(consts_1.CONNECTION_CREATED_CONST, resolve);
161
+ this.em.once(consts_1.CONNECTION_FAILED_CONST, reject);
161
162
  return;
162
163
  }
163
164
  this.creatingConnection = true;
164
- debug('rabbit: creating connection', { HOST, USERNAME, HEARTBEAT });
165
- const connection = await amqp_connection_manager_1.connect([`amqp://${USERNAME}:${PASSWORD}@${HOST}?heartbeat=${HEARTBEAT}}`]);
165
+ let isResolved = false;
166
+ debug('rabbit: creating connection', { host: this.host, userName: this.userName, HEARTBEAT });
167
+ const connection = await amqp_connection_manager_1.connect([`amqp://${this.userName}:${this.password}@${this.host}?heartbeat=${HEARTBEAT}}`]);
166
168
  this.connection = connection;
169
+ this.connection.on('error', (err) => {
170
+ logger_1.default.error('rabbit: connection error', { err });
171
+ if (!isResolved) {
172
+ isResolved = true;
173
+ reject(err);
174
+ this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
175
+ }
176
+ });
177
+ this.connection.on('connectFailed', (err) => {
178
+ logger_1.default.error('rabbit: connection connectFailed', { err });
179
+ if (!isResolved) {
180
+ isResolved = true;
181
+ reject(err);
182
+ this.em.emit(consts_1.CONNECTION_FAILED_CONST, err);
183
+ }
184
+ });
167
185
  this.connection.on('disconnect', ({ err }) => {
168
186
  debug('rabbit: connection closed');
169
187
  this.exchanges = {};
170
188
  this.queues = {};
171
- this.connection = null;
172
- this.channel = null;
173
189
  if (this.options?.disableReconnect) {
174
190
  logger_1.default.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
175
191
  this.blockReconnect = true;
@@ -183,6 +199,7 @@ class RabbitMq {
183
199
  await this.loadConsumers();
184
200
  this.creatingConnection = false;
185
201
  this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
202
+ isResolved = true;
186
203
  resolve(connection);
187
204
  });
188
205
  });
@@ -191,8 +208,13 @@ class RabbitMq {
191
208
  return new Promise(async (resolve, reject) => {
192
209
  const connection = await this.getConnection();
193
210
  const channel = connection.createChannel({});
211
+ let isResolved = false;
194
212
  channel.on('error', (err) => {
195
213
  logger_1.default.error(`rabbit: channel ${name} error`, { err });
214
+ if (!isResolved) {
215
+ isResolved = true;
216
+ reject(err);
217
+ }
196
218
  });
197
219
  channel.on('close', (...args) => {
198
220
  logger_1.default.error(`rabbit: channel ${name} closed`, { args });
@@ -202,6 +224,7 @@ class RabbitMq {
202
224
  });
203
225
  channel.once('connect', () => {
204
226
  debug(`rabbit: channel ${name} CONNECTED`);
227
+ isResolved = true;
205
228
  resolve(channel);
206
229
  });
207
230
  });
@@ -212,10 +235,9 @@ class RabbitMq {
212
235
  return resolve(this.channel);
213
236
  }
214
237
  try {
215
- const channel = await this.getNewChannel({
216
- onClose: () => {
217
- this.channel = null;
218
- },
238
+ const channel = await this.getNewChannel({});
239
+ channel.on('error', (err) => {
240
+ logger_1.default.error('rabbit: channel error', { err });
219
241
  });
220
242
  this.channel = channel;
221
243
  resolve(channel);
@@ -241,14 +263,12 @@ class RabbitMq {
241
263
  return channel.checkQueue(queue);
242
264
  }
243
265
  async deleteQueue(queue) {
244
- return new Promise(async (resolve, reject) => {
245
- RabbitMq.validateName('queue', queue);
246
- const channel = await this.assertChannel();
247
- logger_1.default.info('rabbit: deleting queue', { queue });
248
- const deleteQueueRes = await channel.deleteQueue(queue);
249
- debug('queue deleted', deleteQueueRes);
250
- resolve(deleteQueueRes);
251
- });
266
+ RabbitMq.validateName('queue', queue);
267
+ const channel = await this.assertChannel();
268
+ logger_1.default.info('rabbit: deleting queue', { queue });
269
+ const deleteQueueRes = await channel.deleteQueue(queue);
270
+ debug('queue deleted', deleteQueueRes);
271
+ return deleteQueueRes;
252
272
  }
253
273
  async bindQueue(queue, exchange) {
254
274
  const channel = await this.assertChannel();
@@ -414,12 +434,18 @@ class RabbitMq {
414
434
  }
415
435
  async sendToQueue(queue, content, options, customHeaders, isBlocking) {
416
436
  const callback = async () => {
417
- RabbitMq.validateName('queue', queue);
418
- await this.assertChannel();
419
- await this.assertQueue(queue, options);
420
- const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
421
- debug(`rabbit: sending to queue ${queue}`, { res });
422
- return res;
437
+ try {
438
+ RabbitMq.validateName('queue', queue);
439
+ await this.assertChannel();
440
+ await this.assertQueue(queue, options);
441
+ const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
442
+ debug(`rabbit: sending to queue ${queue}`, { res });
443
+ return res;
444
+ }
445
+ catch (e) {
446
+ logger_1.default.error(`rabbit: failed to send to queue ${queue}`, { e });
447
+ throw e;
448
+ }
423
449
  };
424
450
  if (isBlocking) {
425
451
  return callback();
@@ -6,6 +6,7 @@ export declare const USER_TRACING_HEADER = "x-af-user-id";
6
6
  export declare const USER_OBJECT = "userObject";
7
7
  export declare const DEFAULT_USE_CONSUME_WITH_LOCK = false;
8
8
  export declare const CONNECTION_CREATED_CONST = "connectionCreated";
9
+ export declare const CONNECTION_FAILED_CONST = "connectionFailed";
9
10
  export declare const DEFAULT_OPTIONS: {
10
11
  limit: number;
11
12
  retries: number;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_OPTIONS = 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.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';
@@ -9,6 +9,7 @@ exports.USER_TRACING_HEADER = 'x-af-user-id';
9
9
  exports.USER_OBJECT = 'userObject';
10
10
  exports.DEFAULT_USE_CONSUME_WITH_LOCK = false;
11
11
  exports.CONNECTION_CREATED_CONST = 'connectionCreated';
12
+ exports.CONNECTION_FAILED_CONST = 'connectionFailed';
12
13
  exports.DEFAULT_OPTIONS = {
13
14
  limit: 1,
14
15
  retries: 1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "3.1.1",
3
+ "version": "3.2.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -9,7 +9,7 @@
9
9
  "example": "ts-node src/example.ts",
10
10
  "build": "rm -rf dist && tsc",
11
11
  "linter": "./node_modules/.bin/eslint .",
12
- "test": "jest --forceExit",
12
+ "test": "jest --runInBand --forceExit",
13
13
  "test-local": "RABBITMQ_SERVICE_HOST=localhost:5672 jest --forceExit",
14
14
  "coverage": "jest --coverage --forceExit && rm -rf ./coverage",
15
15
  "dev": "nodemon"
@@ -37,6 +37,7 @@
37
37
  "eslint-config-airbnb-typescript": "^12.0.0",
38
38
  "eslint-plugin-import": "^2.22.1",
39
39
  "jest": "^27.0.5",
40
+ "node-tcp-proxy": "^0.0.28",
40
41
  "ts-jest": "^27.0.3",
41
42
  "ts-node": "^8.6.2",
42
43
  "typescript": "^3.8.3"
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ import getRedisInstance, { RedisConfig } from './lib/redis';
17
17
  import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
18
18
  import {
19
19
  CONNECTION_CREATED_CONST,
20
+ CONNECTION_FAILED_CONST,
20
21
  DEFAULT_LOCK_TIMEOUT,
21
22
  DEFAULT_OPTIONS,
22
23
  RETRY_HEADER,
@@ -37,6 +38,7 @@ import {
37
38
  // const debug = nodeDebug('af-rabbitmq')
38
39
  const debug = logger.info;
39
40
 
41
+ const PUBLISH_TIMEOUT = 1000 * 10;
40
42
  export interface IAfRabbitMq {
41
43
  ack: any;
42
44
  nack: any;
@@ -64,6 +66,8 @@ export interface AfRabbitOptions {
64
66
  * @default false
65
67
  */
66
68
  dontRetryAssert?: boolean;
69
+
70
+ rabbitHost?: string;
67
71
  }
68
72
 
69
73
  type newChannelOpts = {
@@ -82,12 +86,6 @@ type AfConsumer = {
82
86
  options: ConsumeOptions | undefined;
83
87
  }
84
88
 
85
- const USERNAME: string = process.env.RABBITMQ_USERNAME || 'guest';
86
-
87
- const PASSWORD: string = process.env.RABBITMQ_PASSWORD || 'guest';
88
-
89
- const HOST: string = process.env.RABBITMQ_SERVICE_HOST || 'localhost';
90
-
91
89
  const HEARTBEAT = '60';
92
90
 
93
91
  class RabbitMq implements IAfRabbitMq {
@@ -118,6 +116,7 @@ class RabbitMq implements IAfRabbitMq {
118
116
  const outbreakTrace = outbreak.getCurrentContext();
119
117
  return {
120
118
  timestamp: moment().unix(),
119
+ timeout: PUBLISH_TIMEOUT,
121
120
  headers: {
122
121
  creationTimestamp: moment().valueOf(),
123
122
  ...customHeaders,
@@ -127,10 +126,6 @@ class RabbitMq implements IAfRabbitMq {
127
126
  };
128
127
  }
129
128
 
130
- // PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
131
-
132
- // PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
133
-
134
129
  DISCONNECT_MSG = 'rabbit: connection disconnect';
135
130
 
136
131
  RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
@@ -155,12 +150,24 @@ class RabbitMq implements IAfRabbitMq {
155
150
 
156
151
  redisLock?: RedisLockType;
157
152
 
153
+ userName: string;
154
+
155
+ password: string;
156
+
157
+ host: string;
158
+
158
159
  /** Array of consumers tags used for canceling consumption */
159
160
  consumersTags: Array<[ConfirmChannel, string]>;
160
161
 
161
162
  private consumers: Array<AfConsumer> = [];
162
163
 
163
164
  constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig) {
165
+ this.userName = process.env.RABBITMQ_USERNAME || 'guest';
166
+
167
+ this.password = process.env.RABBITMQ_PASSWORD || 'guest';
168
+
169
+ this.host = options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
170
+
164
171
  this.em = new EventEmitter();
165
172
  this.channel = null;
166
173
  this.connection = null;
@@ -259,7 +266,7 @@ class RabbitMq implements IAfRabbitMq {
259
266
  }
260
267
 
261
268
  async getConnection() {
262
- return new Promise<AmqpConnectionManager>(async (resolve) => {
269
+ return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
263
270
  if (this.blockReconnect) {
264
271
  debug('rabbit: block reconnect');
265
272
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -278,18 +285,36 @@ class RabbitMq implements IAfRabbitMq {
278
285
  if (this.creatingConnection) {
279
286
  debug('rabbit: creating connection emi');
280
287
  this.em.once(CONNECTION_CREATED_CONST, resolve);
288
+ this.em.once(CONNECTION_FAILED_CONST, reject);
281
289
  return;
282
290
  }
283
291
  this.creatingConnection = true;
284
- debug('rabbit: creating connection', { HOST, USERNAME, HEARTBEAT });
285
- const connection: AmqpConnectionManager = await connect([`amqp://${USERNAME}:${PASSWORD}@${HOST}?heartbeat=${HEARTBEAT}}`]);
292
+ let isResolved = false;
293
+ debug('rabbit: creating connection', { host: this.host, userName: this.userName, HEARTBEAT });
294
+ const connection: AmqpConnectionManager = await connect([`amqp://${this.userName}:${this.password}@${this.host}?heartbeat=${HEARTBEAT}}`]);
286
295
  this.connection = connection;
296
+ this.connection.on('error', (err) => {
297
+ logger.error('rabbit: connection error', { err });
298
+ if (!isResolved) {
299
+ isResolved = true;
300
+ reject(err);
301
+ this.em.emit(CONNECTION_FAILED_CONST, err);
302
+ }
303
+ });
304
+
305
+ this.connection.on('connectFailed', (err) => {
306
+ logger.error('rabbit: connection connectFailed', { err });
307
+ if (!isResolved) {
308
+ isResolved = true;
309
+ reject(err);
310
+ this.em.emit(CONNECTION_FAILED_CONST, err);
311
+ }
312
+ });
313
+
287
314
  this.connection.on('disconnect', ({ err }) => {
288
315
  debug('rabbit: connection closed');
289
316
  this.exchanges = {};
290
317
  this.queues = {};
291
- this.connection = null;
292
- this.channel = null;
293
318
  if (this.options?.disableReconnect) {
294
319
  logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
295
320
  this.blockReconnect = true;
@@ -302,6 +327,7 @@ class RabbitMq implements IAfRabbitMq {
302
327
  await this.loadConsumers();
303
328
  this.creatingConnection = false;
304
329
  this.em.emit(CONNECTION_CREATED_CONST, connection);
330
+ isResolved = true;
305
331
  resolve(connection);
306
332
  });
307
333
  });
@@ -310,10 +336,16 @@ class RabbitMq implements IAfRabbitMq {
310
336
  async getNewChannel({ name = rand().toString(), onClose = null }: newChannelOpts = {}) {
311
337
  return new Promise<ChannelWrapper>(async (resolve, reject) => {
312
338
  const connection: AmqpConnectionManager = await this.getConnection();
313
- const channel = connection.createChannel({});
339
+ const channel = connection.createChannel({
340
+ });
314
341
 
342
+ let isResolved = false;
315
343
  channel.on('error', (err) => {
316
344
  logger.error(`rabbit: channel ${name} error`, { err });
345
+ if (!isResolved) {
346
+ isResolved = true;
347
+ reject(err);
348
+ }
317
349
  });
318
350
  channel.on('close', (...args) => {
319
351
  logger.error(`rabbit: channel ${name} closed`, { args });
@@ -323,6 +355,7 @@ class RabbitMq implements IAfRabbitMq {
323
355
  });
324
356
  channel.once('connect', () => {
325
357
  debug(`rabbit: channel ${name} CONNECTED`);
358
+ isResolved = true;
326
359
  resolve(channel);
327
360
  });
328
361
  });
@@ -336,9 +369,9 @@ class RabbitMq implements IAfRabbitMq {
336
369
 
337
370
  try {
338
371
  const channel = await this.getNewChannel({
339
- onClose: () => {
340
- this.channel = null;
341
- },
372
+ });
373
+ channel.on('error', (err) => {
374
+ logger.error('rabbit: channel error', { err });
342
375
  });
343
376
  this.channel = channel;
344
377
  resolve(channel);
@@ -366,14 +399,12 @@ class RabbitMq implements IAfRabbitMq {
366
399
  }
367
400
 
368
401
  private async deleteQueue(queue: string) {
369
- return new Promise(async (resolve, reject) => {
370
- RabbitMq.validateName('queue', queue);
371
- const channel: ChannelWrapper = await this.assertChannel();
372
- logger.info('rabbit: deleting queue', { queue });
373
- const deleteQueueRes = await channel.deleteQueue(queue);
374
- debug('queue deleted', deleteQueueRes);
375
- resolve(deleteQueueRes);
376
- });
402
+ RabbitMq.validateName('queue', queue);
403
+ const channel: ChannelWrapper = await this.assertChannel();
404
+ logger.info('rabbit: deleting queue', { queue });
405
+ const deleteQueueRes = await channel.deleteQueue(queue);
406
+ debug('queue deleted', deleteQueueRes);
407
+ return deleteQueueRes;
377
408
  }
378
409
 
379
410
  async bindQueue(queue: string, exchange: string) {
@@ -578,14 +609,19 @@ class RabbitMq implements IAfRabbitMq {
578
609
  isBlocking?: boolean,
579
610
  ): Promise<boolean | undefined> {
580
611
  const callback = async (): Promise<boolean | undefined> => {
581
- RabbitMq.validateName('queue', queue);
582
- await this.assertChannel();
583
- await this.assertQueue(queue, options);
584
- const res = await this.channel?.sendToQueue(queue,
585
- Buffer.from(JSON.stringify(content)),
586
- RabbitMq.getPublishOptions(customHeaders));
587
- debug(`rabbit: sending to queue ${queue}`, { res });
588
- return res;
612
+ try {
613
+ RabbitMq.validateName('queue', queue);
614
+ await this.assertChannel();
615
+ await this.assertQueue(queue, options);
616
+ const res = await this.channel?.sendToQueue(queue,
617
+ Buffer.from(JSON.stringify(content)),
618
+ RabbitMq.getPublishOptions(customHeaders));
619
+ debug(`rabbit: sending to queue ${queue}`, { res });
620
+ return res;
621
+ } catch (e) {
622
+ logger.error(`rabbit: failed to send to queue ${queue}`, { e });
623
+ throw e;
624
+ }
589
625
  };
590
626
  if (isBlocking) {
591
627
  return callback();
package/src/lib/consts.ts CHANGED
@@ -6,6 +6,7 @@ export const USER_TRACING_HEADER = 'x-af-user-id';
6
6
  export const USER_OBJECT = 'userObject';
7
7
  export const DEFAULT_USE_CONSUME_WITH_LOCK = false;
8
8
  export const CONNECTION_CREATED_CONST = 'connectionCreated';
9
+ export const CONNECTION_FAILED_CONST = 'connectionFailed';
9
10
  export const DEFAULT_OPTIONS = {
10
11
  limit: 1,
11
12
  retries: 1,