@autofleet/rabbit 2.1.8-beta1 → 2.1.8-beta10

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
@@ -2,15 +2,35 @@
2
2
  import { Options } from 'amqplib';
3
3
  import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
4
4
  import { EventEmitter } from 'events';
5
+ import { RedisConfig } from './redis';
6
+ export interface IAfRabbitMq {
7
+ ack: any;
8
+ nack: any;
9
+ assertChannel: any;
10
+ assertExchange: any;
11
+ assertQueue: any;
12
+ consume: any;
13
+ consumeFromExchange: any;
14
+ publish: any;
15
+ sendToQueue: any;
16
+ redisClient?: any;
17
+ }
5
18
  interface ExchangesCache {
6
19
  [key: string]: any;
7
20
  }
21
+ declare type CustomMessageHeaders = {
22
+ redisTimestampValidationKey?: string;
23
+ };
8
24
  export interface AfRabbitOptions {
9
25
  reconnect: boolean;
10
26
  }
11
- declare class RabbitMq {
27
+ declare class RabbitMq implements IAfRabbitMq {
12
28
  static parseMsg(msg: any): any;
13
29
  static validateName(type: string, name: string): void;
30
+ static getPublishOptions(customHeaders?: CustomMessageHeaders): {
31
+ timestamp: number;
32
+ headers: CustomMessageHeaders;
33
+ };
14
34
  PUBLISH_TIMEOUT: number;
15
35
  PUBLISH_ERROR_MSG: string;
16
36
  DISCONNECT_MSG: string;
@@ -21,19 +41,22 @@ declare class RabbitMq {
21
41
  creatingConnection: boolean;
22
42
  exchanges: ExchangesCache;
23
43
  options: AfRabbitOptions | undefined;
24
- constructor(options?: AfRabbitOptions);
25
- ack: (msg: any) => Promise<void>;
26
- nack: (queue: string, options: any, deadQueueOptions: Options.AssertQueue) => (msg: any, { skipRetry, }?: any) => Promise<void>;
44
+ redisClient: any;
45
+ constructor(options?: AfRabbitOptions, redisConfig?: RedisConfig);
46
+ private shouldConsumeMessageByTimestamp;
47
+ ack: (channel: ChannelWrapper, shouldUpdateRedisTimestamp?: boolean) => (msg: any) => Promise<void>;
48
+ nack: (channel: ChannelWrapper, queue: string, options: any, deadQueueOptions: Options.AssertQueue) => (msg: any, { skipRetry, }?: any) => Promise<void>;
27
49
  getConnection(): Promise<AmqpConnectionManager>;
28
50
  getNewChannel(): Promise<ChannelWrapper>;
29
51
  assertChannel(): Promise<ChannelWrapper>;
30
52
  assertExchange(exchangeName: string, options?: any): Promise<any>;
31
- getQueueLength(queue: string): Promise<any>;
32
- bindQueue(queue: string, exchange: string): Promise<any>;
53
+ getQueueLength(queue: string): Promise<import("amqplib").Replies.AssertQueue>;
54
+ bindQueue(queue: string, exchange: string): Promise<void>;
33
55
  assertQueue(queue: string, options?: Options.AssertQueue): Promise<void>;
34
56
  consume(queue: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
35
57
  consumeFromExchange(queue: string, exchange: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
36
- publish(exchange: string, content: any): Promise<unknown>;
37
- sendToQueue(queue: string, content: any, options?: any): Promise<void>;
58
+ publish(exchange: string, content: any, customHeaders?: any): Promise<unknown>;
59
+ sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<boolean>;
60
+ isConnected(): Promise<boolean>;
38
61
  }
39
62
  export default RabbitMq;
package/dist/index.js CHANGED
@@ -34,31 +34,49 @@ Object.defineProperty(exports, "__esModule", { value: true });
34
34
  /* eslint-disable @typescript-eslint/ban-ts-comment,@typescript-eslint/ban-types,@typescript-eslint/no-unused-vars,consistent-return,no-async-promise-executor,no-param-reassign,no-empty */
35
35
  // eslint-disable-next-line max-classes-per-file
36
36
  const bluebird = __importStar(require("bluebird"));
37
+ const moment_1 = __importDefault(require("moment"));
37
38
  const amqp_connection_manager_1 = require("amqp-connection-manager");
38
39
  const events_1 = require("events");
39
- const outbreak_1 = require("@autofleet/outbreak");
40
+ // import { newTrace, traceTypes } from '@autofleet/outbreak';
41
+ const logger_1 = __importDefault(require("./logger"));
40
42
  const rabbitError_1 = __importDefault(require("./rabbitError"));
43
+ const redis_1 = __importDefault(require("./redis"));
41
44
  const DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 48;
42
45
  const defaultOptions = {
43
46
  limit: 1,
44
47
  retries: 1,
45
48
  deadMessageTtl: DEFAULT_DEAD_TTL_TWO_DAYS,
46
49
  };
50
+ const SECONDS_TO_MILLISECONDS = 1000;
47
51
  const assertExchangeFanout = (c, exchangeName) => c.assertExchange(exchangeName, 'fanout');
48
52
  const connectionCreatedConst = 'connectionCreated';
49
53
  class RabbitMq {
50
- constructor(options) {
54
+ constructor(options, redisConfig) {
51
55
  this.PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
52
56
  this.PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
53
57
  this.DISCONNECT_MSG = 'rabbit: connection disconnect';
54
58
  this.RESCONNECT_MSG = 'rabbit: reconnecting';
55
- this.ack = (msg) => __awaiter(this, void 0, void 0, function* () {
56
- if (this.channel) {
57
- yield this.channel.ack(msg);
59
+ this.shouldConsumeMessageByTimestamp = (msg) => __awaiter(this, void 0, void 0, function* () {
60
+ const { properties: { timestamp, headers } } = msg;
61
+ if (timestamp && (headers === null || headers === void 0 ? void 0 : headers.redisTimestampValidationKey) && this.redisClient) {
62
+ const lastMessageTimestamp = yield this.redisClient.getAsync(headers.redisTimestampValidationKey);
63
+ logger_1.default.info('rabbit: checking if should consume params', {
64
+ msg, timestamp, headers, lastMessageTimestamp,
65
+ });
66
+ return !lastMessageTimestamp || (parseInt(lastMessageTimestamp, 10) <= parseInt(timestamp, 10));
67
+ }
68
+ return true;
69
+ });
70
+ this.ack = (channel, shouldUpdateRedisTimestamp = false) => (msg) => __awaiter(this, void 0, void 0, function* () {
71
+ yield channel.ack(msg);
72
+ const { properties: { timestamp, headers } } = msg;
73
+ if (shouldUpdateRedisTimestamp && timestamp && (headers === null || headers === void 0 ? void 0 : headers.redisTimestampValidationKey) && this.redisClient) {
74
+ const parsedTimestamp = parseInt(timestamp, 10);
75
+ yield this.redisClient.setAsync(headers.redisTimestampValidationKey, parsedTimestamp, 'EX', 3600);
58
76
  }
59
77
  });
60
- this.nack = (queue, options, deadQueueOptions) => (msg, { skipRetry = false, } = {}) => __awaiter(this, void 0, void 0, function* () {
61
- if (this.channel) {
78
+ this.nack = (channel, queue, options, deadQueueOptions) => (msg, { skipRetry = false, } = {}) => __awaiter(this, void 0, void 0, function* () {
79
+ if (channel) {
62
80
  if (!skipRetry
63
81
  && (!msg.content['x-retry-count']
64
82
  || msg.content['x-retry-count'] < options.retries)) {
@@ -70,7 +88,7 @@ class RabbitMq {
70
88
  yield this.assertQueue(deadQueue, deadQueueOptions);
71
89
  yield this.sendToQueue(deadQueue, msg.content, deadQueueOptions);
72
90
  }
73
- yield this.channel.ack(msg);
91
+ yield channel.ack(msg);
74
92
  }
75
93
  });
76
94
  this.em = new events_1.EventEmitter();
@@ -79,6 +97,7 @@ class RabbitMq {
79
97
  this.creatingConnection = false;
80
98
  this.exchanges = {};
81
99
  this.options = options;
100
+ this.redisClient = redisConfig && redis_1.default(redisConfig);
82
101
  }
83
102
  static parseMsg(msg) {
84
103
  let { content } = msg;
@@ -94,19 +113,27 @@ class RabbitMq {
94
113
  throw new rabbitError_1.default(`error while using ${type} with no name`);
95
114
  }
96
115
  }
116
+ static getPublishOptions(customHeaders = {}) {
117
+ return {
118
+ timestamp: moment_1.default().unix(),
119
+ headers: customHeaders,
120
+ };
121
+ }
97
122
  getConnection() {
98
123
  return __awaiter(this, void 0, void 0, function* () {
99
124
  return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
100
125
  if (this.creatingConnection) {
101
- this.em.on(connectionCreatedConst, resolve);
126
+ this.em.once(connectionCreatedConst, resolve);
102
127
  return;
103
128
  }
104
129
  if (this.connection !== null) {
105
- return this.connection;
130
+ return resolve(this.connection);
106
131
  }
107
132
  this.creatingConnection = true;
133
+ const username = process.env.RABBITMQ_USERNAME || 'guest';
134
+ const password = process.env.RABBITMQ_PASSWORD || 'guest';
108
135
  const host = process.env.RABBITMQ_SERVICE_HOST || '';
109
- const connection = yield amqp_connection_manager_1.connect([`amqp://${host}`]);
136
+ const connection = yield amqp_connection_manager_1.connect([`amqp://${username}:${password}@${host}`]);
110
137
  this.connection = connection;
111
138
  this.connection.on('disconnect', ({ err }) => {
112
139
  var _a;
@@ -136,8 +163,8 @@ class RabbitMq {
136
163
  if (this.channel) {
137
164
  return resolve(this.channel);
138
165
  }
139
- const connection = yield this.getConnection();
140
166
  try {
167
+ const connection = yield this.getConnection();
141
168
  if (this.channel === null) {
142
169
  this.channel = connection.createChannel({});
143
170
  }
@@ -194,10 +221,21 @@ class RabbitMq {
194
221
  yield c.assertQueue(queue);
195
222
  yield c.prefetch(limit, true);
196
223
  return Promise.all([
197
- c.consume(queue, (msg) => {
198
- outbreak_1.newTrace(outbreak_1.traceTypes.RABBIT);
199
- callback(RabbitMq.parseMsg(msg), this.ack, this.nack(queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
200
- }),
224
+ c.consume(queue, (msg) => __awaiter(this, void 0, void 0, function* () {
225
+ const parsedMessage = RabbitMq.parseMsg(msg);
226
+ // newTrace(traceTypes.RABBIT);
227
+ const shouldConsume = yield this.shouldConsumeMessageByTimestamp(parsedMessage);
228
+ logger_1.default.info('rabbit: trying to consume', { queue, shouldConsume, msg });
229
+ if (!shouldConsume) {
230
+ return this.ack(channel)(msg);
231
+ }
232
+ try {
233
+ yield callback(parsedMessage, this.ack(channel, true), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
234
+ }
235
+ catch (e) {
236
+ yield this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl });
237
+ }
238
+ })),
201
239
  ]);
202
240
  }));
203
241
  });
@@ -216,31 +254,49 @@ class RabbitMq {
216
254
  yield c.prefetch(limit, true);
217
255
  return Promise.all([
218
256
  c.bindQueue(queue, exchange, ''),
219
- c.consume(queue, (msg) => {
220
- outbreak_1.newTrace(outbreak_1.traceTypes.RABBIT);
221
- callback(RabbitMq.parseMsg(msg), this.ack, this.nack(queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
222
- }),
257
+ c.consume(queue, (msg) => __awaiter(this, void 0, void 0, function* () {
258
+ const parsedMessage = RabbitMq.parseMsg(msg);
259
+ // newTrace(traceTypes.RABBIT);
260
+ const shouldConsume = yield this.shouldConsumeMessageByTimestamp(parsedMessage);
261
+ logger_1.default.info('rabbit: trying to consume', { queue, shouldConsume, msg });
262
+ if (!shouldConsume) {
263
+ return this.ack(channel)(msg);
264
+ }
265
+ callback(parsedMessage, this.ack(channel, true), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
266
+ })),
223
267
  ]);
224
268
  }));
225
269
  });
226
270
  }
227
- publish(exchange, content) {
271
+ publish(exchange, content, customHeaders) {
228
272
  return __awaiter(this, void 0, void 0, function* () {
229
273
  RabbitMq.validateName('exchange', exchange);
230
274
  const channel = yield this.assertChannel();
231
275
  yield this.assertExchange(exchange);
232
- return new bluebird.Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
233
- yield channel.publish(exchange, '', Buffer.from(JSON.stringify(content)));
234
- return resolve();
276
+ return new bluebird.Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
277
+ try {
278
+ yield channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
279
+ return resolve();
280
+ }
281
+ catch (e) {
282
+ reject(e);
283
+ }
235
284
  })).timeout(this.PUBLISH_TIMEOUT, `${this.PUBLISH_ERROR_MSG}${exchange}`);
236
285
  });
237
286
  }
238
- sendToQueue(queue, content, options) {
287
+ sendToQueue(queue, content, options, customHeaders) {
239
288
  return __awaiter(this, void 0, void 0, function* () {
240
289
  RabbitMq.validateName('queue', queue);
241
290
  const channel = yield this.assertChannel();
242
291
  yield this.assertQueue(queue, options);
243
- return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)));
292
+ logger_1.default.info('rabbit: sending to queue', { queue, content, customHeaders });
293
+ return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
294
+ });
295
+ }
296
+ isConnected() {
297
+ return __awaiter(this, void 0, void 0, function* () {
298
+ const connection = yield this.getConnection();
299
+ return connection.isConnected();
244
300
  });
245
301
  }
246
302
  }
@@ -0,0 +1,2 @@
1
+ declare const logger: any;
2
+ export default logger;
package/dist/logger.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Logger = require('@autofleet/logger');
4
+ const logger = Logger();
5
+ exports.default = logger;
package/dist/mock.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import 'jest';
2
- declare class RabbitMq {
3
- ack: jest.Mock<unknown>;
4
- nack: jest.Mock<unknown>;
5
- assertChannel: jest.Mock<unknown>;
6
- assertExchange: jest.Mock<unknown>;
7
- assertQueue: jest.Mock<unknown>;
8
- consume: jest.Mock<unknown>;
9
- consumeFromExchange: jest.Mock<unknown>;
10
- publish: jest.Mock<unknown>;
11
- sendToQueue: jest.Mock<unknown>;
2
+ import { IAfRabbitMq } from './index';
3
+ declare class RabbitMq implements IAfRabbitMq {
4
+ ack: any;
5
+ nack: any;
6
+ assertChannel: any;
7
+ assertExchange: any;
8
+ assertQueue: any;
9
+ consume: any;
10
+ consumeFromExchange: any;
11
+ publish: any;
12
+ sendToQueue: any;
12
13
  }
13
14
  export default RabbitMq;
package/dist/mock.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ // eslint-disable-next-line import/no-extraneous-dependencies
3
4
  require("jest");
4
5
  class RabbitMq {
5
6
  constructor() {
@@ -0,0 +1,7 @@
1
+ export declare type RedisConfig = {
2
+ host: string;
3
+ port: number | undefined;
4
+ prefix?: string;
5
+ };
6
+ declare const getRedisInstance: (config: RedisConfig) => any;
7
+ export default getRedisInstance;
package/dist/redis.js ADDED
@@ -0,0 +1,11 @@
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
+ const bluebird_1 = __importDefault(require("bluebird"));
7
+ const redis = require('redis');
8
+ bluebird_1.default.promisifyAll(redis.RedisClient.prototype);
9
+ bluebird_1.default.promisifyAll(redis.Multi.prototype);
10
+ const getRedisInstance = (config) => redis.createClient(config);
11
+ exports.default = getRedisInstance;
package/dump.rdb CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "2.1.8-beta1",
3
+ "version": "2.1.8-beta10",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
package/src/index.ts CHANGED
@@ -5,7 +5,7 @@ import moment from 'moment';
5
5
  import { ConfirmChannel, Options } from 'amqplib';
6
6
  import { AmqpConnectionManager, ChannelWrapper, connect } from 'amqp-connection-manager';
7
7
  import { EventEmitter } from 'events';
8
- import { newTrace, traceTypes } from '@autofleet/outbreak';
8
+ // import { newTrace, traceTypes } from '@autofleet/outbreak';
9
9
  import logger from './logger';
10
10
  import RabbitError from './rabbitError';
11
11
  import getRedisInstance, { RedisConfig } from './redis';
@@ -253,13 +253,21 @@ class RabbitMq implements IAfRabbitMq {
253
253
  queue,
254
254
  async (msg: any) => {
255
255
  const parsedMessage = RabbitMq.parseMsg(msg);
256
- newTrace(traceTypes.RABBIT);
256
+ // newTrace(traceTypes.RABBIT);
257
257
  const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
258
258
  logger.info('rabbit: trying to consume', { queue, shouldConsume, msg });
259
259
  if (!shouldConsume) {
260
260
  return this.ack(channel)(msg);
261
261
  }
262
- callback(parsedMessage, this.ack(channel, true), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
262
+ try {
263
+ await callback(
264
+ parsedMessage,
265
+ this.ack(channel, true),
266
+ this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }),
267
+ );
268
+ } catch (e) {
269
+ await this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl });
270
+ }
263
271
  },
264
272
  ),
265
273
  ]);
@@ -284,7 +292,7 @@ class RabbitMq implements IAfRabbitMq {
284
292
  queue,
285
293
  async (msg: any) => {
286
294
  const parsedMessage = RabbitMq.parseMsg(msg);
287
- newTrace(traceTypes.RABBIT);
295
+ // newTrace(traceTypes.RABBIT);
288
296
  const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
289
297
  logger.info('rabbit: trying to consume', { queue, shouldConsume, msg });
290
298
  if (!shouldConsume) {