@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 +66 -0
- package/dist/example.js +19 -8
- package/dist/index.d.ts +6 -2
- package/dist/index.js +137 -83
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +64 -0
- package/dist/mock.d.ts +9 -9
- package/dist/rabbitError.d.ts +3 -0
- package/dist/rabbitError.js +9 -0
- package/jest.config.js +14 -0
- package/package.json +15 -5
- package/src/example.ts +8 -6
- package/src/index.test.ts +59 -0
- package/src/index.ts +58 -20
- package/src/mock.ts +9 -1
- package/src/rabbitError.ts +6 -0
- package/tsconfig.json +2 -5
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
+
yield rabbit.consumeFromExchange('test-q', 'test-e', (msg, ack) => {
|
|
11
22
|
console.log('msg', msg.content);
|
|
12
23
|
ack(msg);
|
|
13
24
|
});
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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:
|
|
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<
|
|
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.
|
|
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
|
-
|
|
25
|
+
yield this.channel.ack(msg);
|
|
11
26
|
}
|
|
12
|
-
};
|
|
13
|
-
this.nack = (queue) =>
|
|
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
|
-
|
|
33
|
+
yield this.sendToQueue(queue, msg.content);
|
|
18
34
|
}
|
|
19
35
|
else {
|
|
20
36
|
const deadQueue = `${queue}-dead`;
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
yield this.assertQueue(deadQueue);
|
|
38
|
+
yield this.sendToQueue(deadQueue, msg.content);
|
|
23
39
|
}
|
|
24
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
61
|
-
return
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
return
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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;
|
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.
|
|
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
|
-
"
|
|
22
|
+
"bluebird": "^3.7.2",
|
|
23
|
+
"jest": "^25.3.0"
|
|
19
24
|
},
|
|
20
25
|
"devDependencies": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
|
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;
|