@autofleet/rabbit 2.0.4-beta3 → 2.0.4-beta5

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.
@@ -0,0 +1,54 @@
1
+ /// <reference types="node" />
2
+ import { Options } from 'amqplib';
3
+ import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
4
+ import { EventEmitter } from 'events';
5
+ export interface IAfRabbitMq {
6
+ ack: any;
7
+ nack: any;
8
+ assertChannel: any;
9
+ assertExchange: any;
10
+ assertQueue: any;
11
+ consume: any;
12
+ consumeFromExchange: any;
13
+ publish: any;
14
+ sendToQueue: any;
15
+ }
16
+ interface ExchangesCache {
17
+ [key: string]: any;
18
+ }
19
+ export interface AfRabbitOptions {
20
+ reconnect: boolean;
21
+ }
22
+ declare class RabbitMq implements IAfRabbitMq {
23
+ static parseMsg(msg: any): any;
24
+ static validateName(type: string, name: string): void;
25
+ PUBLISH_TIMEOUT: number;
26
+ PUBLISH_ERROR_MSG: string;
27
+ DISCONNECT_MSG: string;
28
+ RESCONNECT_MSG: string;
29
+ channel: ChannelWrapper | null;
30
+ connection: AmqpConnectionManager | null;
31
+ em: EventEmitter;
32
+ creatingConnection: boolean;
33
+ exchanges: ExchangesCache;
34
+ options: AfRabbitOptions | undefined;
35
+ logger: any;
36
+ constructor({ options, logger }?: {
37
+ options?: AfRabbitOptions;
38
+ logger?: any;
39
+ });
40
+ ack: (channel: ChannelWrapper) => (msg: any) => Promise<void>;
41
+ nack: (channel: ChannelWrapper, queue: string, options: any, deadQueueOptions: Options.AssertQueue) => (msg: any, { skipRetry, }?: any) => Promise<void>;
42
+ getConnection(): Promise<AmqpConnectionManager>;
43
+ getNewChannel(): Promise<ChannelWrapper>;
44
+ assertChannel(): Promise<ChannelWrapper>;
45
+ assertExchange(exchangeName: string, options?: any): Promise<any>;
46
+ getQueueLength(queue: string): Promise<any>;
47
+ bindQueue(queue: string, exchange: string): Promise<any>;
48
+ assertQueue(queue: string, options?: Options.AssertQueue): Promise<void>;
49
+ consume(queue: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
50
+ consumeFromExchange(queue: string, exchange: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
51
+ publish(exchange: string, content: any): Promise<unknown>;
52
+ sendToQueue(queue: string, content: any, options?: any): Promise<void>;
53
+ }
54
+ export default RabbitMq;
package/dist/index.js ADDED
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
23
+ return new (P || (P = Promise))(function (resolve, reject) {
24
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
25
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
26
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
27
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
28
+ });
29
+ };
30
+ var __importDefault = (this && this.__importDefault) || function (mod) {
31
+ return (mod && mod.__esModule) ? mod : { "default": mod };
32
+ };
33
+ Object.defineProperty(exports, "__esModule", { value: true });
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
+ // eslint-disable-next-line max-classes-per-file
36
+ const bluebird = __importStar(require("bluebird"));
37
+ const amqp_connection_manager_1 = require("amqp-connection-manager");
38
+ const events_1 = require("events");
39
+ const outbreak_1 = require("@autofleet/outbreak");
40
+ const rabbitError_1 = __importDefault(require("./rabbitError"));
41
+ const DEFAULT_DEAD_TTL_TWO_DAYS = 60000 * 60 * 48;
42
+ const defaultOptions = {
43
+ limit: 1,
44
+ retries: 1,
45
+ deadMessageTtl: DEFAULT_DEAD_TTL_TWO_DAYS,
46
+ };
47
+ const assertExchangeFanout = (c, exchangeName) => c.assertExchange(exchangeName, 'fanout');
48
+ const callbackWithTimeout = (callback, args, timeout, timeoutMessage = 'Timeout while ack message') => __awaiter(void 0, void 0, void 0, function* () {
49
+ yield new bluebird.Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
50
+ try {
51
+ yield callback(...args);
52
+ return resolve();
53
+ }
54
+ catch (e) {
55
+ return reject(e);
56
+ }
57
+ })).timeout(timeout, timeoutMessage);
58
+ });
59
+ const connectionCreatedConst = 'connectionCreated';
60
+ class RabbitMq {
61
+ constructor({ options, logger } = {}) {
62
+ this.PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
63
+ this.PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
64
+ this.DISCONNECT_MSG = 'rabbit: connection disconnect';
65
+ this.RESCONNECT_MSG = 'rabbit: reconnecting';
66
+ this.ack = (channel) => (msg) => __awaiter(this, void 0, void 0, function* () {
67
+ yield channel.ack(msg);
68
+ });
69
+ this.nack = (channel, queue, options, deadQueueOptions) => (msg, { skipRetry = false, } = {}) => __awaiter(this, void 0, void 0, function* () {
70
+ if (channel) {
71
+ if (!skipRetry
72
+ && (!msg.content['x-retry-count']
73
+ || msg.content['x-retry-count'] < options.retries)) {
74
+ msg.content['x-retry-count'] = msg.content['x-retry-count'] ? msg.content['x-retry-count'] + 1 : 1;
75
+ yield this.sendToQueue(queue, msg.content);
76
+ }
77
+ else {
78
+ const deadQueue = `${queue}-dead`;
79
+ yield this.assertQueue(deadQueue, deadQueueOptions);
80
+ yield this.sendToQueue(deadQueue, msg.content, deadQueueOptions);
81
+ }
82
+ yield channel.ack(msg);
83
+ }
84
+ });
85
+ this.em = new events_1.EventEmitter();
86
+ this.channel = null;
87
+ this.connection = null;
88
+ this.creatingConnection = false;
89
+ this.exchanges = {};
90
+ this.options = options;
91
+ this.logger = logger;
92
+ }
93
+ static parseMsg(msg) {
94
+ let { content } = msg;
95
+ content = content.toString();
96
+ try {
97
+ content = JSON.parse(content);
98
+ }
99
+ catch (e) { }
100
+ return Object.assign(Object.assign({}, msg), { content });
101
+ }
102
+ static validateName(type, name) {
103
+ if (!name || name === '') {
104
+ throw new rabbitError_1.default(`error while using ${type} with no name`);
105
+ }
106
+ }
107
+ getConnection() {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
110
+ if (this.creatingConnection) {
111
+ this.em.on(connectionCreatedConst, resolve);
112
+ return;
113
+ }
114
+ if (this.connection !== null) {
115
+ return resolve(this.connection);
116
+ }
117
+ this.creatingConnection = true;
118
+ const host = process.env.RABBITMQ_SERVICE_HOST || '';
119
+ const connection = yield amqp_connection_manager_1.connect([`amqp://${host}`]);
120
+ this.connection = connection;
121
+ this.connection.on('disconnect', ({ err }) => {
122
+ var _a;
123
+ if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.reconnect) {
124
+ console.error(`${this.RESCONNECT_MSG}${err && ` - ${err}`}`);
125
+ this.connection = null;
126
+ }
127
+ else {
128
+ console.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
129
+ }
130
+ });
131
+ this.creatingConnection = false;
132
+ this.em.emit(connectionCreatedConst, connection);
133
+ resolve(connection);
134
+ }));
135
+ });
136
+ }
137
+ getNewChannel() {
138
+ return __awaiter(this, void 0, void 0, function* () {
139
+ const connection = yield this.getConnection();
140
+ return connection.createChannel({});
141
+ });
142
+ }
143
+ assertChannel() {
144
+ return __awaiter(this, void 0, void 0, function* () {
145
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
146
+ if (this.channel) {
147
+ return resolve(this.channel);
148
+ }
149
+ const connection = yield this.getConnection();
150
+ try {
151
+ if (this.channel === null) {
152
+ this.channel = connection.createChannel({});
153
+ }
154
+ resolve(this.channel);
155
+ }
156
+ catch (e) {
157
+ reject(e);
158
+ }
159
+ }));
160
+ });
161
+ }
162
+ assertExchange(exchangeName, options) {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ const channel = yield this.assertChannel();
165
+ if (this.exchanges[exchangeName]) {
166
+ return this.exchanges[exchangeName];
167
+ }
168
+ return channel.addSetup((c) => __awaiter(this, void 0, void 0, function* () {
169
+ const exchange = yield assertExchangeFanout(c, exchangeName);
170
+ this.exchanges[exchangeName] = exchange;
171
+ return exchange;
172
+ }));
173
+ });
174
+ }
175
+ getQueueLength(queue) {
176
+ return __awaiter(this, void 0, void 0, function* () {
177
+ RabbitMq.validateName('queue', queue);
178
+ const channel = yield this.assertChannel();
179
+ // @ts-ignore
180
+ return channel.checkQueue(queue);
181
+ });
182
+ }
183
+ bindQueue(queue, exchange) {
184
+ return __awaiter(this, void 0, void 0, function* () {
185
+ const channel = yield this.assertChannel();
186
+ // @ts-ignore
187
+ return channel.bindQueue(queue, exchange, '');
188
+ });
189
+ }
190
+ assertQueue(queue, options) {
191
+ return __awaiter(this, void 0, void 0, function* () {
192
+ RabbitMq.validateName('queue', queue);
193
+ const channel = yield this.assertChannel();
194
+ return channel.addSetup((c) => c.assertQueue(queue, options));
195
+ });
196
+ }
197
+ consume(queue, callback, options) {
198
+ return __awaiter(this, void 0, void 0, function* () {
199
+ const optionsWithDefaults = Object.assign(Object.assign({}, defaultOptions), options);
200
+ RabbitMq.validateName('queue', queue);
201
+ const { limit, deadMessageTtl } = optionsWithDefaults;
202
+ const channel = yield this.getNewChannel();
203
+ return channel.addSetup((c) => __awaiter(this, void 0, void 0, function* () {
204
+ yield c.assertQueue(queue);
205
+ yield c.prefetch(limit, true);
206
+ return Promise.all([
207
+ c.consume(queue, (msg) => __awaiter(this, void 0, void 0, function* () {
208
+ var _a;
209
+ outbreak_1.newTrace(outbreak_1.traceTypes.RABBIT);
210
+ if (optionsWithDefaults.timeout) {
211
+ const args = [
212
+ RabbitMq.parseMsg(msg),
213
+ this.ack(channel),
214
+ this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }),
215
+ ];
216
+ try {
217
+ yield callbackWithTimeout(callback, args, optionsWithDefaults.timeout, optionsWithDefaults.timeoutMessage);
218
+ }
219
+ catch (e) {
220
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.error('message nacked because of error in callbackWithTimeout', {
221
+ error: e,
222
+ msg,
223
+ });
224
+ this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl });
225
+ }
226
+ }
227
+ else {
228
+ callback(RabbitMq.parseMsg(msg), this.ack(channel), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
229
+ }
230
+ })),
231
+ ]);
232
+ }));
233
+ });
234
+ }
235
+ consumeFromExchange(queue, exchange, callback, options) {
236
+ return __awaiter(this, void 0, void 0, function* () {
237
+ const optionsWithDefaults = Object.assign(Object.assign({}, defaultOptions), options);
238
+ RabbitMq.validateName('exchange', exchange);
239
+ RabbitMq.validateName('queue', queue);
240
+ const { limit, deadMessageTtl } = optionsWithDefaults;
241
+ const channel = yield this.getNewChannel();
242
+ return channel.addSetup((c) => __awaiter(this, void 0, void 0, function* () {
243
+ const assertExchange = yield assertExchangeFanout(c, exchange);
244
+ yield c.assertQueue(queue);
245
+ this.exchanges[exchange] = assertExchange;
246
+ yield c.prefetch(limit, true);
247
+ return Promise.all([
248
+ c.bindQueue(queue, exchange, ''),
249
+ c.consume(queue, (msg) => {
250
+ outbreak_1.newTrace(outbreak_1.traceTypes.RABBIT);
251
+ callback(RabbitMq.parseMsg(msg), this.ack(channel), this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl }));
252
+ }),
253
+ ]);
254
+ }));
255
+ });
256
+ }
257
+ publish(exchange, content) {
258
+ return __awaiter(this, void 0, void 0, function* () {
259
+ RabbitMq.validateName('exchange', exchange);
260
+ const channel = yield this.assertChannel();
261
+ yield this.assertExchange(exchange);
262
+ return new bluebird.Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
263
+ try {
264
+ yield channel.publish(exchange, '', Buffer.from(JSON.stringify(content)));
265
+ return resolve();
266
+ }
267
+ catch (e) {
268
+ reject(e);
269
+ }
270
+ })).timeout(this.PUBLISH_TIMEOUT, `${this.PUBLISH_ERROR_MSG}${exchange}`);
271
+ });
272
+ }
273
+ sendToQueue(queue, content, options) {
274
+ return __awaiter(this, void 0, void 0, function* () {
275
+ RabbitMq.validateName('queue', queue);
276
+ const channel = yield this.assertChannel();
277
+ yield this.assertQueue(queue, options);
278
+ return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)));
279
+ });
280
+ }
281
+ }
282
+ exports.default = RabbitMq;
package/dist/mock.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import 'jest';
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;
13
+ }
14
+ export default RabbitMq;
package/dist/mock.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // eslint-disable-next-line import/no-extraneous-dependencies
4
+ require("jest");
5
+ class RabbitMq {
6
+ constructor() {
7
+ this.ack = jest.fn();
8
+ this.nack = jest.fn();
9
+ this.assertChannel = jest.fn();
10
+ this.assertExchange = jest.fn();
11
+ this.assertQueue = jest.fn();
12
+ this.consume = jest.fn();
13
+ this.consumeFromExchange = jest.fn();
14
+ this.publish = jest.fn();
15
+ this.sendToQueue = jest.fn();
16
+ }
17
+ }
18
+ exports.default = RabbitMq;
@@ -0,0 +1,3 @@
1
+ export default class RabbitError extends Error {
2
+ constructor(message: string);
3
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class RabbitError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = 'RabbitError';
7
+ }
8
+ }
9
+ exports.default = RabbitError;
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "2.0.4-beta3",
3
+ "version": "2.0.4-beta5",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
7
  "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
8
  "start": "ts-node src/index.ts",
9
9
  "example": "ts-node src/example.ts",
10
- "build": "tsc",
10
+ "build": "rm -rf dist && tsc",
11
+ "prepublish": "npm run build",
11
12
  "linter": "./node_modules/.bin/eslint .",
12
13
  "test": "jest --forceExit",
13
14
  "test-local": "RABBITMQ_SERVICE_HOST=10.132.0.98:5672 jest --forceExit",
package/src/index.ts CHANGED
@@ -38,17 +38,14 @@ const defaultOptions = {
38
38
  const assertExchangeFanout = (c: any, exchangeName: string) => c.assertExchange(exchangeName, 'fanout');
39
39
 
40
40
  const callbackWithTimeout = async (callback: any, args: any[], timeout: number, timeoutMessage = 'Timeout while ack message') => {
41
- const callbackPromise = new bluebird.Promise(callback(...args));
42
- Promise.race([
43
- callbackPromise,
44
- new Promise((resolve, reject) => {
45
- setTimeout(() => {
46
- callbackPromise.cancel();
47
- console.log('callbackPromise', callbackPromise.isCancelled());
48
- reject(timeoutMessage);
49
- }, timeout);
50
- }),
51
- ]);
41
+ await new bluebird.Promise(async (resolve, reject) => {
42
+ try {
43
+ await callback(...args);
44
+ return resolve();
45
+ } catch (e) {
46
+ return reject(e);
47
+ }
48
+ }).timeout(timeout, timeoutMessage);
52
49
  };
53
50
 
54
51
  const connectionCreatedConst = 'connectionCreated';
@@ -93,13 +90,16 @@ class RabbitMq implements IAfRabbitMq {
93
90
 
94
91
  options: AfRabbitOptions | undefined;
95
92
 
96
- constructor(options?: AfRabbitOptions) {
93
+ logger: any;
94
+
95
+ constructor({ options, logger }: { options?: AfRabbitOptions, logger?: any} = {}) {
97
96
  this.em = new EventEmitter();
98
97
  this.channel = null;
99
98
  this.connection = null;
100
99
  this.creatingConnection = false;
101
100
  this.exchanges = {};
102
101
  this.options = options;
102
+ this.logger = logger;
103
103
  }
104
104
 
105
105
  public ack = (channel: ChannelWrapper) => async (msg: any) => {
@@ -124,7 +124,6 @@ class RabbitMq implements IAfRabbitMq {
124
124
  || msg.content['x-retry-count'] < options.retries)
125
125
  ) {
126
126
  msg.content['x-retry-count'] = msg.content['x-retry-count'] ? msg.content['x-retry-count'] + 1 : 1;
127
- // msg.content.nackCount = msg.content.nackCount ? msg.content.nackCount + 1 : 1;
128
127
  await this.sendToQueue(queue, msg.content);
129
128
  } else {
130
129
  const deadQueue = `${queue}-dead`;
@@ -240,6 +239,10 @@ class RabbitMq implements IAfRabbitMq {
240
239
  try {
241
240
  await callbackWithTimeout(callback, args, optionsWithDefaults.timeout, optionsWithDefaults.timeoutMessage);
242
241
  } catch (e) {
242
+ this.logger?.error('message nacked because of error in callbackWithTimeout', {
243
+ error: e,
244
+ msg,
245
+ });
243
246
  this.nack(channel, queue, optionsWithDefaults, { messageTtl: deadMessageTtl });
244
247
  }
245
248
  } else {