@autofleet/rabbit 1.4.1 → 1.5.0-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/.eslintrc.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "root": true,
3
+ "env": {
4
+ "node": true,
5
+ "jest": true
6
+ },
7
+ "extends": [
8
+ "airbnb-base",
9
+ "plugin:@typescript-eslint/recommended"
10
+ ],
11
+ "rules": {
12
+ "import/extensions": [
13
+ "error",
14
+ "ignorePackages",
15
+ {
16
+ "js": "never",
17
+ "jsx": "never",
18
+ "ts": "never",
19
+ "tsx": "never"
20
+ }
21
+ ],
22
+ "@typescript-eslint/no-unused-vars": ["error"],
23
+ "@typescript-eslint/no-var-requires": ["off"],
24
+ "@typescript-eslint/camelcase": ["off"],
25
+ "max-len": ["off"],
26
+ "@typescript-eslint/explicit-function-return-type": ["off"]
27
+ },
28
+ "overrides": [
29
+ {
30
+ // enable the rule specifically for TypeScript files
31
+ "files": ["src/**.js", "src/**.ts"],
32
+ "rules": {
33
+ "@typescript-eslint/explicit-function-return-type": ["off"],
34
+ "@typescript-eslint/no-var-requires": ["off"],
35
+ "@typescript-eslint/camelcase": ["off"]
36
+ }
37
+ }
38
+ ],
39
+ "parser": "@typescript-eslint/parser",
40
+ "parserOptions": {
41
+ "project": "./tsconfig.json",
42
+ "sourceType": "module"
43
+ },
44
+ "ignorePatterns": ["dist/*", "node_modules/", "jest.config.js", "migrations/*", "newrelic.js"],
45
+ "plugins": ["@typescript-eslint"],
46
+ "settings": {
47
+ "import/extensions": [
48
+ "error",
49
+ "ignorePackages",
50
+ {
51
+ "js": "never",
52
+ "jsx": "never",
53
+ "ts": "never",
54
+ "tsx": "never"
55
+ }
56
+ ],
57
+ "import/parsers": {
58
+ "@typescript-eslint/parser": [".ts",".tsx"]
59
+ },
60
+ "import/resolver": {
61
+ "node": {
62
+ "extensions": [".js",".jsx",".ts",".tsx", ".json"]
63
+ }
64
+ }
65
+ }
66
+ }
package/dist/example.js CHANGED
@@ -1,22 +1,33 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  const index_1 = require("./index");
4
- const init = async () => {
13
+ // process.env.RABBITMQ_SERVICE_HOST="34.76.223.229:5672";
14
+ const init = () => __awaiter(void 0, void 0, void 0, function* () {
5
15
  const rabbit = new index_1.default();
6
- await rabbit.consumeFromExchange('test-q2', 'test-e', (msg, ack) => {
16
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
+ yield rabbit.consumeFromExchange('test-q2', 'test-e', (msg, ack) => {
7
18
  console.log('msg2', msg.content);
8
19
  // ack(msg);
9
20
  });
10
- await rabbit.consumeFromExchange('test-q', 'test-e', (msg, ack) => {
21
+ yield rabbit.consumeFromExchange('test-q', 'test-e', (msg, ack) => {
11
22
  console.log('msg', msg.content);
12
23
  ack(msg);
13
24
  });
14
- await rabbit.publish('test-e', 'hey');
15
- await rabbit.publish('test-e', 'hey1');
16
- await rabbit.consume('test-q-2', (msg, ack) => {
25
+ yield rabbit.publish('test-e', 'hey');
26
+ yield rabbit.publish('test-e', 'hey1');
27
+ yield rabbit.consume('test-q-2', (msg, ack) => {
17
28
  console.log('msg-q', msg.content);
18
29
  ack(msg);
19
30
  });
20
- await rabbit.sendToQueue('test-q-2', 'hey-q');
21
- };
31
+ yield rabbit.sendToQueue('test-q-2', 'hey-q');
32
+ });
22
33
  init();
package/dist/index.d.ts CHANGED
@@ -6,10 +6,14 @@ interface ExchangesCache {
6
6
  }
7
7
  declare class RabbitMq {
8
8
  static parseMsg(msg: any): any;
9
+ static validateName(type: string, name: string): void;
10
+ PUBLISH_TIMEOUT: number;
11
+ PUBLISH_ERROR_MSG: string;
12
+ DISCONNECT_MSG: string;
9
13
  channel: ChannelWrapper | null;
10
14
  connection: AmqpConnectionManager | null;
11
15
  em: EventEmitter;
12
- creatingConnection: Boolean;
16
+ creatingConnection: boolean;
13
17
  exchanges: ExchangesCache;
14
18
  constructor();
15
19
  ack: (msg: any) => Promise<void>;
@@ -20,7 +24,7 @@ declare class RabbitMq {
20
24
  assertQueue(queue: string, options?: any): Promise<void>;
21
25
  consume(queue: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
22
26
  consumeFromExchange(queue: string, exchange: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any): Promise<void>;
23
- publish(exchange: string, content: any): Promise<void>;
27
+ publish(exchange: string, content: any): Promise<unknown>;
24
28
  sendToQueue(queue: string, content: any): Promise<void>;
25
29
  }
26
30
  export default RabbitMq;
package/dist/index.js CHANGED
@@ -1,30 +1,46 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ // eslint-disable-next-line max-classes-per-file
13
+ const bluebird = require("bluebird");
3
14
  const amqp_connection_manager_1 = require("amqp-connection-manager");
4
15
  const events_1 = require("events");
16
+ const rabbitError_1 = require("./rabbitError");
5
17
  const connectionCreatedConst = 'connectionCreated';
6
18
  class RabbitMq {
7
19
  constructor() {
8
- this.ack = async (msg) => {
20
+ this.PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
21
+ this.PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
22
+ this.DISCONNECT_MSG = 'rabbit: connection disconnect';
23
+ this.ack = (msg) => __awaiter(this, void 0, void 0, function* () {
9
24
  if (this.channel) {
10
- await this.channel.ack(msg);
25
+ yield this.channel.ack(msg);
11
26
  }
12
- };
13
- this.nack = (queue) => async (msg, retries) => {
27
+ });
28
+ this.nack = (queue) => (msg, retries) => __awaiter(this, void 0, void 0, function* () {
14
29
  if (this.channel) {
15
30
  if (!msg.content['x-retry-count'] || msg.content['x-retry-count'] <= retries) {
31
+ // eslint-disable-next-line no-param-reassign
16
32
  msg.content['x-retry-count'] = msg.content['x-retry-count'] ? msg.content['x-retry-count'] + 1 : 1;
17
- await this.sendToQueue(queue, msg.content);
33
+ yield this.sendToQueue(queue, msg.content);
18
34
  }
19
35
  else {
20
36
  const deadQueue = `${queue}-dead`;
21
- await this.assertQueue(deadQueue);
22
- await this.sendToQueue(deadQueue, msg.content);
37
+ yield this.assertQueue(deadQueue);
38
+ yield this.sendToQueue(deadQueue, msg.content);
23
39
  }
24
- await this.channel.ack(msg);
40
+ yield this.channel.ack(msg);
25
41
  }
26
- };
27
- this.em = new events_1.EventEmitter;
42
+ });
43
+ this.em = new events_1.EventEmitter();
28
44
  this.channel = null;
29
45
  this.connection = null;
30
46
  this.creatingConnection = false;
@@ -36,94 +52,132 @@ class RabbitMq {
36
52
  try {
37
53
  content = JSON.parse(content);
38
54
  }
39
- catch (e) { }
40
- return Object.assign({}, msg, { content });
55
+ catch (e) { } // eslint-disable-line no-empty
56
+ return Object.assign(Object.assign({}, msg), { content });
41
57
  }
42
- async getConnection() {
43
- return new Promise(async (resolve) => {
44
- if (this.creatingConnection) {
45
- this.em.on(connectionCreatedConst, resolve);
46
- return;
47
- }
48
- if (this.connection !== null) {
49
- return this.connection;
50
- }
51
- this.creatingConnection = true;
52
- let host = process.env.RABBITMQ_SERVICE_HOST || '35.240.13.28';
53
- let connection = await amqp_connection_manager_1.connect([`amqp://${host}`]);
54
- this.connection = connection;
55
- this.creatingConnection = false;
56
- this.em.emit(connectionCreatedConst, connection);
57
- resolve(connection);
58
+ static validateName(type, name) {
59
+ if (!name || name === '') {
60
+ throw new rabbitError_1.default(`error while using ${type} with no name`);
61
+ }
62
+ }
63
+ getConnection() {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ // eslint-disable-next-line no-async-promise-executor
66
+ return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
67
+ if (this.creatingConnection) {
68
+ this.em.on(connectionCreatedConst, resolve);
69
+ return;
70
+ }
71
+ if (this.connection !== null) {
72
+ // eslint-disable-next-line consistent-return
73
+ return this.connection;
74
+ }
75
+ this.creatingConnection = true;
76
+ const host = process.env.RABBITMQ_SERVICE_HOST || '';
77
+ const connection = yield amqp_connection_manager_1.connect([`amqp://${host}`]);
78
+ this.connection = connection;
79
+ this.connection.on('disconnect', ({ err }) => console.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`));
80
+ this.creatingConnection = false;
81
+ this.em.emit(connectionCreatedConst, connection);
82
+ resolve(connection);
83
+ }));
58
84
  });
59
85
  }
60
- async assertChannel() {
61
- return new Promise(async (resolve, reject) => {
62
- if (this.channel) {
63
- return resolve(this.channel);
64
- }
65
- let connection = await this.getConnection();
66
- try {
67
- if (this.channel === null) {
68
- this.channel = connection.createChannel({});
86
+ assertChannel() {
87
+ return __awaiter(this, void 0, void 0, function* () {
88
+ // eslint-disable-next-line no-async-promise-executor,consistent-return
89
+ return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
90
+ if (this.channel) {
91
+ return resolve(this.channel);
69
92
  }
70
- resolve(this.channel);
71
- }
72
- catch (e) {
73
- reject(e);
74
- }
93
+ const connection = yield this.getConnection();
94
+ try {
95
+ if (this.channel === null) {
96
+ this.channel = connection.createChannel({});
97
+ }
98
+ resolve(this.channel);
99
+ }
100
+ catch (e) {
101
+ reject(e);
102
+ }
103
+ }));
75
104
  });
76
105
  }
77
- async assertExchange(exchangeName, options) {
78
- const channel = await this.assertChannel();
79
- if (this.exchanges[exchangeName]) {
80
- return this.exchanges[exchangeName];
81
- }
82
- return channel.addSetup(async (c) => {
83
- const exchange = await c.assertExchange(exchangeName, 'fanout');
84
- this.exchanges[exchangeName] = exchange;
85
- return exchange;
106
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
107
+ assertExchange(exchangeName, options) {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ const channel = yield this.assertChannel();
110
+ if (this.exchanges[exchangeName]) {
111
+ return this.exchanges[exchangeName];
112
+ }
113
+ return channel.addSetup((c) => __awaiter(this, void 0, void 0, function* () {
114
+ const exchange = yield c.assertExchange(exchangeName, 'fanout');
115
+ this.exchanges[exchangeName] = exchange;
116
+ return exchange;
117
+ }));
86
118
  });
87
119
  }
88
- async assertQueue(queue, options) {
89
- const channel = await this.assertChannel();
90
- return channel.addSetup((c) => c.assertQueue(queue));
120
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
121
+ assertQueue(queue, options) {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ RabbitMq.validateName('queue', queue);
124
+ const channel = yield this.assertChannel();
125
+ return channel.addSetup((c) => c.assertQueue(queue));
126
+ });
91
127
  }
92
- async consume(queue, callback, options) {
93
- const { limit } = (options && options.limit) ? options : { limit: 1 };
94
- const channel = await this.assertChannel();
95
- await this.assertQueue(queue);
96
- return channel.addSetup(async (c) => {
97
- await c.prefetch(limit, true);
98
- return Promise.all([
99
- c.consume(queue, (msg) => callback(RabbitMq.parseMsg(msg), this.ack, this.nack(queue))),
100
- ]);
128
+ // eslint-disable-next-line @typescript-eslint/ban-types
129
+ consume(queue, callback, options) {
130
+ return __awaiter(this, void 0, void 0, function* () {
131
+ RabbitMq.validateName('queue', queue);
132
+ const { limit } = (options && options.limit) ? options : { limit: 1 };
133
+ const channel = yield this.assertChannel();
134
+ yield this.assertQueue(queue);
135
+ return channel.addSetup((c) => __awaiter(this, void 0, void 0, function* () {
136
+ yield c.prefetch(limit, true);
137
+ return Promise.all([
138
+ c.consume(queue, (msg) => callback(RabbitMq.parseMsg(msg), this.ack, this.nack(queue))),
139
+ ]);
140
+ }));
101
141
  });
102
142
  }
103
- async consumeFromExchange(queue, exchange, callback, options) {
104
- const { limit } = (options && options.limit) ? options : { limit: 1 };
105
- const channel = await this.assertChannel();
106
- await Promise.all([
107
- this.assertExchange(exchange),
108
- this.assertQueue(queue),
109
- ]);
110
- return channel.addSetup(async (c) => {
111
- await c.prefetch(limit, true);
112
- return Promise.all([
113
- c.bindQueue(queue, exchange, ''),
114
- c.consume(queue, (msg) => callback(RabbitMq.parseMsg(msg), this.ack, this.nack(queue))),
143
+ // eslint-disable-next-line @typescript-eslint/ban-types
144
+ consumeFromExchange(queue, exchange, callback, options) {
145
+ return __awaiter(this, void 0, void 0, function* () {
146
+ RabbitMq.validateName('queue', queue);
147
+ RabbitMq.validateName('exchange', exchange);
148
+ const { limit } = (options && options.limit) ? options : { limit: 1 };
149
+ const channel = yield this.assertChannel();
150
+ yield Promise.all([
151
+ this.assertExchange(exchange),
152
+ this.assertQueue(queue),
115
153
  ]);
154
+ return channel.addSetup((c) => __awaiter(this, void 0, void 0, function* () {
155
+ yield c.prefetch(limit, true);
156
+ return Promise.all([
157
+ c.bindQueue(queue, exchange, ''),
158
+ c.consume(queue, (msg) => callback(RabbitMq.parseMsg(msg), this.ack, this.nack(queue))),
159
+ ]);
160
+ }));
116
161
  });
117
162
  }
118
- async publish(exchange, content) {
119
- const channel = await this.assertChannel();
120
- await this.assertExchange(exchange);
121
- return channel.publish(exchange, '', Buffer.from(JSON.stringify(content)));
163
+ publish(exchange, content) {
164
+ return __awaiter(this, void 0, void 0, function* () {
165
+ RabbitMq.validateName('exchange', exchange);
166
+ const channel = yield this.assertChannel();
167
+ yield this.assertExchange(exchange);
168
+ return new bluebird.Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
169
+ yield channel.publish(exchange, '', Buffer.from(JSON.stringify(content)));
170
+ return resolve();
171
+ })).timeout(this.PUBLISH_TIMEOUT, `${this.PUBLISH_ERROR_MSG}${exchange}`);
172
+ });
122
173
  }
123
- async sendToQueue(queue, content) {
124
- const channel = await this.assertChannel();
125
- await this.assertQueue(queue);
126
- return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)));
174
+ sendToQueue(queue, content) {
175
+ return __awaiter(this, void 0, void 0, function* () {
176
+ RabbitMq.validateName('queue', queue);
177
+ const channel = yield this.assertChannel();
178
+ yield this.assertQueue(queue);
179
+ return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)));
180
+ });
127
181
  }
128
182
  }
129
183
  exports.default = RabbitMq;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const index_1 = require("./index");
13
+ // import RabbitError from './rabbitError';
14
+ jest.setTimeout(120000);
15
+ const rabbit = new index_1.default();
16
+ const delay = () => new Promise((resolve) => setTimeout(() => resolve(), 500));
17
+ describe('RabbitMQ', () => {
18
+ it('check exchange', () => __awaiter(void 0, void 0, void 0, function* () {
19
+ const callback = (msg, ack) => {
20
+ expect(msg.content).toEqual('hey');
21
+ ack(msg);
22
+ };
23
+ const mockFn = jest.fn(callback);
24
+ yield rabbit.consumeFromExchange('test-q2', 'test-e', mockFn);
25
+ yield rabbit.publish('test-e', 'hey');
26
+ yield delay();
27
+ expect(mockFn).toBeCalled();
28
+ }));
29
+ it('check queue', () => __awaiter(void 0, void 0, void 0, function* () {
30
+ const callback = (msg, ack) => {
31
+ expect(msg.content).toEqual('hey-q');
32
+ ack(msg);
33
+ };
34
+ const mockFn = jest.fn(callback);
35
+ yield rabbit.consume('test-q-2', mockFn);
36
+ yield rabbit.sendToQueue('test-q-2', 'hey-q');
37
+ yield delay();
38
+ expect(mockFn).toBeCalled();
39
+ }));
40
+ it('cant publish to unknown exchange', () => __awaiter(void 0, void 0, void 0, function* () {
41
+ let err;
42
+ try {
43
+ yield rabbit.publish('', 'hey');
44
+ }
45
+ catch (e) {
46
+ err = e;
47
+ }
48
+ expect(err).toBeDefined();
49
+ }));
50
+ it('cant publish due timeout', () => __awaiter(void 0, void 0, void 0, function* () {
51
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
52
+ // @ts-ignore
53
+ process.env.RABBITMQ_PUBLISH_TIMEOUT = 1;
54
+ const rabbit2 = new index_1.default();
55
+ let err;
56
+ try {
57
+ yield rabbit2.publish('test-e', 'hey');
58
+ }
59
+ catch (e) {
60
+ err = e;
61
+ }
62
+ expect(err).toBeDefined();
63
+ }));
64
+ });
package/dist/mock.d.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import 'jest';
2
2
  declare class RabbitMq {
3
- ack: jest.Mock<{}>;
4
- nack: jest.Mock<{}>;
5
- assertChannel: jest.Mock<{}>;
6
- assertExchange: jest.Mock<{}>;
7
- assertQueue: jest.Mock<{}>;
8
- consume: jest.Mock<{}>;
9
- consumeFromExchange: jest.Mock<{}>;
10
- publish: jest.Mock<{}>;
11
- sendToQueue: jest.Mock<{}>;
3
+ ack: jest.Mock<any, any>;
4
+ nack: jest.Mock<any, any>;
5
+ assertChannel: jest.Mock<any, any>;
6
+ assertExchange: jest.Mock<any, any>;
7
+ assertQueue: jest.Mock<any, any>;
8
+ consume: jest.Mock<any, any>;
9
+ consumeFromExchange: jest.Mock<any, any>;
10
+ publish: jest.Mock<any, any>;
11
+ sendToQueue: jest.Mock<any, any>;
12
12
  }
13
13
  export 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/jest.config.js ADDED
@@ -0,0 +1,14 @@
1
+ module.exports = {
2
+ testEnvironment: 'node',
3
+ roots: ['<rootDir>/src'],
4
+ transform: {
5
+ '^.+\\.tsx?$': 'ts-jest',
6
+ },
7
+ testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(tsx?$|jsx?$)',
8
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
9
+ coverageThreshold: {
10
+ global: {
11
+ lines: 75,
12
+ },
13
+ },
14
+ }
package/package.json CHANGED
@@ -1,25 +1,35 @@
1
1
  {
2
2
  "name": "@autofleet/rabbit",
3
- "version": "1.4.1",
3
+ "version": "1.5.0-1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
7
  "start": "ts-node src/index.ts",
8
8
  "example": "ts-node src/example.ts",
9
9
  "prepublish": "tsc",
10
+ "linter": "./node_modules/.bin/eslint .",
11
+ "test": "jest --forceExit",
12
+ "test-local": "RABBITMQ_SERVICE_HOST=34.76.223.229:5672 jest --forceExit",
13
+ "coverage": "jest --coverage --forceExit --runInBand && rm -rf ./coverage",
10
14
  "dev": "nodemon"
11
15
  },
12
16
  "dependencies": {
17
+ "@types/jest": "^23.0.0",
13
18
  "@types/amqp-connection-manager": "^2.0.2",
14
19
  "@types/amqplib": "^0.5.9",
15
- "@types/jest": "^23.3.10",
16
20
  "amqp-connection-manager": "^3.2.1",
17
21
  "amqplib": "^0.6.0",
18
- "jest": "^23.6.0"
22
+ "bluebird": "^3.7.2",
23
+ "jest": "^25.3.0"
19
24
  },
20
25
  "devDependencies": {
21
- "ts-node": "^7.0.1",
22
- "typescript": "^3.2.1"
26
+ "@typescript-eslint/eslint-plugin": "^4.8.1",
27
+ "eslint": "^7.13.0",
28
+ "eslint-config-airbnb-typescript": "^12.0.0",
29
+ "eslint-plugin-import": "^2.22.1",
30
+ "ts-jest": "^25.4.0",
31
+ "ts-node": "^8.6.2",
32
+ "typescript": "^3.8.3"
23
33
  },
24
34
  "author": "",
25
35
  "license": "ISC"
package/src/example.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import RabbitMq from './index';
2
2
 
3
+ // process.env.RABBITMQ_SERVICE_HOST="34.76.223.229:5672";
4
+
3
5
  const init = async () => {
4
6
  const rabbit = new RabbitMq();
7
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
5
8
  await rabbit.consumeFromExchange('test-q2', 'test-e', (msg, ack) => {
6
9
  console.log('msg2', msg.content);
7
10
  // ack(msg);
@@ -11,15 +14,14 @@ const init = async () => {
11
14
  console.log('msg', msg.content);
12
15
  ack(msg);
13
16
  });
14
- await rabbit.publish('test-e', 'hey')
15
- await rabbit.publish('test-e', 'hey1')
16
-
17
+ await rabbit.publish('test-e', 'hey');
18
+ await rabbit.publish('test-e', 'hey1');
17
19
 
18
20
  await rabbit.consume('test-q-2', (msg, ack) => {
19
21
  console.log('msg-q', msg.content);
20
22
  ack(msg);
21
23
  });
22
- await rabbit.sendToQueue('test-q-2', 'hey-q')
23
- }
24
+ await rabbit.sendToQueue('test-q-2', 'hey-q');
25
+ };
24
26
 
25
- init();
27
+ init();
@@ -0,0 +1,59 @@
1
+ import RabbitMq from './index';
2
+ // import RabbitError from './rabbitError';
3
+
4
+ jest.setTimeout(120000);
5
+
6
+ const rabbit = new RabbitMq();
7
+
8
+ const delay = () => new Promise((resolve) => setTimeout(() => resolve(), 500));
9
+
10
+ describe('RabbitMQ', () => {
11
+ it('check exchange', async () => {
12
+ const callback = (msg: any, ack: any) => {
13
+ expect(msg.content).toEqual('hey');
14
+ ack(msg);
15
+ };
16
+ const mockFn = jest.fn(callback);
17
+ await rabbit.consumeFromExchange('test-q2', 'test-e', mockFn);
18
+ await rabbit.publish('test-e', 'hey');
19
+ await delay();
20
+ expect(mockFn).toBeCalled();
21
+ });
22
+
23
+ it('check queue', async () => {
24
+ const callback = (msg: any, ack: any) => {
25
+ expect(msg.content).toEqual('hey-q');
26
+ ack(msg);
27
+ };
28
+ const mockFn = jest.fn(callback);
29
+ await rabbit.consume('test-q-2', mockFn);
30
+ await rabbit.sendToQueue('test-q-2', 'hey-q');
31
+ await delay();
32
+ expect(mockFn).toBeCalled();
33
+ });
34
+
35
+ it('cant publish to unknown exchange', async () => {
36
+ let err;
37
+ try {
38
+ await rabbit.publish('', 'hey');
39
+ } catch (e) {
40
+ err = e;
41
+ }
42
+ expect(err).toBeDefined();
43
+ });
44
+
45
+ it('cant publish due timeout', async () => {
46
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
47
+ // @ts-ignore
48
+ process.env.RABBITMQ_PUBLISH_TIMEOUT = 1;
49
+ const rabbit2 = new RabbitMq();
50
+
51
+ let err;
52
+ try {
53
+ await rabbit2.publish('test-e', 'hey');
54
+ } catch (e) {
55
+ err = e;
56
+ }
57
+ expect(err).toBeDefined();
58
+ });
59
+ });
package/src/index.ts CHANGED
@@ -1,6 +1,9 @@
1
+ // eslint-disable-next-line max-classes-per-file
2
+ import * as bluebird from 'bluebird';
1
3
  import { ConfirmChannel } from 'amqplib';
2
- import { connect, AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
4
+ import { AmqpConnectionManager, ChannelWrapper, connect } from 'amqp-connection-manager';
3
5
  import { EventEmitter } from 'events';
6
+ import RabbitError from './rabbitError';
4
7
 
5
8
  interface ExchangesCache {
6
9
  [key: string]: any
@@ -14,22 +17,38 @@ class RabbitMq {
14
17
 
15
18
  try {
16
19
  content = JSON.parse(content);
17
- } catch (e) { }
20
+ } catch (e) { } // eslint-disable-line no-empty
18
21
 
19
22
  return {
20
23
  ...msg,
21
24
  content,
25
+ };
26
+ }
27
+
28
+ static validateName(type: string, name: string) {
29
+ if (!name || name === '') {
30
+ throw new RabbitError(`error while using ${type} with no name`);
22
31
  }
23
32
  }
24
-
33
+
34
+ PUBLISH_TIMEOUT = Number(process.env.RABBITMQ_PUBLISH_TIMEOUT) || 60000;
35
+
36
+ PUBLISH_ERROR_MSG = `rabbit: publish timeout(${this.PUBLISH_TIMEOUT}ms) has pass, exchange: `;
37
+
38
+ DISCONNECT_MSG = 'rabbit: connection disconnect';
39
+
25
40
  channel: ChannelWrapper | null;
41
+
26
42
  connection: AmqpConnectionManager | null;
43
+
27
44
  em: EventEmitter;
28
- creatingConnection: Boolean;
45
+
46
+ creatingConnection: boolean;
47
+
29
48
  exchanges: ExchangesCache;
30
49
 
31
50
  constructor() {
32
- this.em = new EventEmitter;
51
+ this.em = new EventEmitter();
33
52
  this.channel = null;
34
53
  this.connection = null;
35
54
  this.creatingConnection = false;
@@ -45,6 +64,7 @@ class RabbitMq {
45
64
  public nack = (queue: string) => async (msg: any, retries: number) => {
46
65
  if (this.channel) {
47
66
  if (!msg.content['x-retry-count'] || msg.content['x-retry-count'] <= retries) {
67
+ // eslint-disable-next-line no-param-reassign
48
68
  msg.content['x-retry-count'] = msg.content['x-retry-count'] ? msg.content['x-retry-count'] + 1 : 1;
49
69
  await this.sendToQueue(queue, msg.content);
50
70
  } else {
@@ -57,31 +77,36 @@ class RabbitMq {
57
77
  }
58
78
 
59
79
  async getConnection() {
80
+ // eslint-disable-next-line no-async-promise-executor
60
81
  return new Promise<AmqpConnectionManager>(async (resolve) => {
61
82
  if (this.creatingConnection) {
62
83
  this.em.on(connectionCreatedConst, resolve);
63
84
  return;
64
85
  }
65
86
  if (this.connection !== null) {
87
+ // eslint-disable-next-line consistent-return
66
88
  return this.connection;
67
89
  }
68
90
  this.creatingConnection = true;
69
- let host: string = process.env.RABBITMQ_SERVICE_HOST || '35.240.13.28';
70
- let connection: AmqpConnectionManager = await connect([`amqp://${host}`]);
91
+ const host: string = process.env.RABBITMQ_SERVICE_HOST || '';
92
+ const connection: AmqpConnectionManager = await connect([`amqp://${host}`]);
71
93
  this.connection = connection;
94
+ this.connection.on('disconnect',
95
+ ({ err }) => console.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`));
72
96
  this.creatingConnection = false;
73
97
  this.em.emit(connectionCreatedConst, connection);
74
98
  resolve(connection);
75
- })
99
+ });
76
100
  }
77
101
 
78
102
  async assertChannel() {
103
+ // eslint-disable-next-line no-async-promise-executor,consistent-return
79
104
  return new Promise<ChannelWrapper>(async (resolve, reject) => {
80
- if(this.channel) {
105
+ if (this.channel) {
81
106
  return resolve(this.channel);
82
107
  }
83
108
 
84
- let connection: AmqpConnectionManager = await this.getConnection();
109
+ const connection: AmqpConnectionManager = await this.getConnection();
85
110
 
86
111
  try {
87
112
  if (this.channel === null) {
@@ -89,29 +114,34 @@ class RabbitMq {
89
114
  }
90
115
  resolve(this.channel);
91
116
  } catch (e) {
92
- reject(e)
117
+ reject(e);
93
118
  }
94
- })
119
+ });
95
120
  }
96
121
 
122
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
97
123
  async assertExchange(exchangeName: string, options?: any) {
98
124
  const channel: ChannelWrapper = await this.assertChannel();
99
- if(this.exchanges[exchangeName]) {
100
- return this.exchanges[exchangeName]
125
+ if (this.exchanges[exchangeName]) {
126
+ return this.exchanges[exchangeName];
101
127
  }
102
128
  return channel.addSetup(async (c: ConfirmChannel) => {
103
- const exchange = await c.assertExchange(exchangeName, 'fanout')
129
+ const exchange = await c.assertExchange(exchangeName, 'fanout');
104
130
  this.exchanges[exchangeName] = exchange;
105
131
  return exchange;
106
132
  });
107
133
  }
108
134
 
135
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
109
136
  async assertQueue(queue: string, options?: any) {
137
+ RabbitMq.validateName('queue', queue);
110
138
  const channel: ChannelWrapper = await this.assertChannel();
111
139
  return channel.addSetup((c: ConfirmChannel) => c.assertQueue(queue));
112
140
  }
113
141
 
142
+ // eslint-disable-next-line @typescript-eslint/ban-types
114
143
  async consume(queue: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any) {
144
+ RabbitMq.validateName('queue', queue);
115
145
  const { limit } = (options && options.limit) ? options : { limit: 1 };
116
146
  const channel: ChannelWrapper = await this.assertChannel();
117
147
  await this.assertQueue(queue);
@@ -123,34 +153,42 @@ class RabbitMq {
123
153
  });
124
154
  }
125
155
 
156
+ // eslint-disable-next-line @typescript-eslint/ban-types
126
157
  async consumeFromExchange(queue: string, exchange: string, callback: (msg: any, ack: Function, nack: Function) => any, options?: any) {
158
+ RabbitMq.validateName('queue', queue);
159
+ RabbitMq.validateName('exchange', exchange);
127
160
  const { limit } = (options && options.limit) ? options : { limit: 1 };
128
161
  const channel: ChannelWrapper = await this.assertChannel();
129
162
  await Promise.all([
130
163
  this.assertExchange(exchange),
131
164
  this.assertQueue(queue),
132
165
  ]);
133
-
166
+
134
167
  return channel.addSetup(async (c: ConfirmChannel) => {
135
168
  await c.prefetch(limit, true);
136
169
  return Promise.all([
137
170
  c.bindQueue(queue, exchange, ''),
138
171
  c.consume(queue, (msg: any) => callback(RabbitMq.parseMsg(msg), this.ack, this.nack(queue))),
139
- ])
140
- })
172
+ ]);
173
+ });
141
174
  }
142
175
 
143
176
  async publish(exchange: string, content: any) {
177
+ RabbitMq.validateName('exchange', exchange);
144
178
  const channel: ChannelWrapper = await this.assertChannel();
145
179
  await this.assertExchange(exchange);
146
- return channel.publish(exchange, '', Buffer.from(JSON.stringify(content)));
180
+ return new bluebird.Promise(async (resolve) => {
181
+ await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)));
182
+ return resolve();
183
+ }).timeout(this.PUBLISH_TIMEOUT, `${this.PUBLISH_ERROR_MSG}${exchange}`);
147
184
  }
148
185
 
149
186
  async sendToQueue(queue: string, content: any) {
187
+ RabbitMq.validateName('queue', queue);
150
188
  const channel: ChannelWrapper = await this.assertChannel();
151
189
  await this.assertQueue(queue);
152
190
  return channel.sendToQueue(queue, Buffer.from(JSON.stringify(content)));
153
191
  }
154
192
  }
155
193
 
156
- export default RabbitMq;
194
+ export default RabbitMq;
package/src/mock.ts CHANGED
@@ -2,14 +2,22 @@ import 'jest';
2
2
 
3
3
  class RabbitMq {
4
4
  public ack = jest.fn();
5
+
5
6
  public nack = jest.fn();
7
+
6
8
  public assertChannel = jest.fn();
9
+
7
10
  public assertExchange = jest.fn();
11
+
8
12
  public assertQueue = jest.fn();
13
+
9
14
  public consume = jest.fn();
15
+
10
16
  public consumeFromExchange = jest.fn();
17
+
11
18
  public publish = jest.fn();
19
+
12
20
  public sendToQueue = jest.fn();
13
21
  }
14
22
 
15
- export default RabbitMq;
23
+ export default RabbitMq;
@@ -0,0 +1,6 @@
1
+ export default class RabbitError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = 'RabbitError';
5
+ }
6
+ }
package/tsconfig.json CHANGED
@@ -1,12 +1,9 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "target": "es2017",
3
+ "target": "es6",
4
4
  "module": "commonjs",
5
- "lib": [
6
- "es2017"
7
- ],
8
5
  "declaration": true,
9
6
  "outDir": "./dist",
10
7
  "strict": true
11
8
  }
12
- }
9
+ }