arnavmq 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +1 -1
- package/.prettierignore +19 -0
- package/README.md +2 -3
- package/package.json +10 -3
- package/src/index.js +5 -2
- package/src/modules/arnavmq.js +3 -3
- package/src/modules/connection.js +35 -31
- package/src/modules/consumer.js +50 -42
- package/src/modules/producer.js +29 -39
- package/src/modules/utils.js +5 -4
package/.github/workflows/ci.yml
CHANGED
package/.prettierignore
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/bin/**
|
|
2
|
+
/build/**
|
|
3
|
+
/coverage/**
|
|
4
|
+
/docs/**
|
|
5
|
+
/jsdoc/**
|
|
6
|
+
/templates/**
|
|
7
|
+
/tests/bench/**
|
|
8
|
+
/tests/fixtures/**
|
|
9
|
+
/tests/performance/**
|
|
10
|
+
/tmp/**
|
|
11
|
+
/public/**
|
|
12
|
+
/node_modules/**
|
|
13
|
+
/lib-cov/**
|
|
14
|
+
/.grunt/**
|
|
15
|
+
/.sonar/**
|
|
16
|
+
/logs/**
|
|
17
|
+
/.idea/**
|
|
18
|
+
/.github/**
|
|
19
|
+
/samples/**
|
package/README.md
CHANGED
|
@@ -62,12 +62,11 @@ arnavmq.subscribe('queue:name', function (msg) {
|
|
|
62
62
|
You can create RPC requests easily by adding the `rpc: true` option to the `produce` call:
|
|
63
63
|
|
|
64
64
|
```javascript
|
|
65
|
-
arnavmq.subscribe('queue:name', function() {
|
|
65
|
+
arnavmq.subscribe('queue:name', function () {
|
|
66
66
|
return 'hello world!'; // you can also return a promise if you want to do async stuff
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
arnavmq.publish('queue:name', { message: 'content' }, { rpc: true, timeout: 1000 })
|
|
70
|
-
.then(function(consumerResponse) {
|
|
69
|
+
arnavmq.publish('queue:name', { message: 'content' }, { rpc: true, timeout: 1000 }).then(function (consumerResponse) {
|
|
71
70
|
console.log(consumerResponse); // prints hello world!
|
|
72
71
|
});
|
|
73
72
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arnavmq",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "ArnavMQ is a RabbitMQ wrapper",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rabbitmq",
|
|
@@ -13,10 +13,15 @@
|
|
|
13
13
|
],
|
|
14
14
|
"main": "src/index.js",
|
|
15
15
|
"scripts": {
|
|
16
|
-
"lint": "eslint .",
|
|
16
|
+
"lint": "eslint . && prettier -c .",
|
|
17
|
+
"format": "eslint --fix . && prettier --write .",
|
|
17
18
|
"cover": "test -d .nyc_output && nyc report --reporter lcov",
|
|
18
19
|
"test": "nyc mocha --recursive --timeout=30000 --exit"
|
|
19
20
|
},
|
|
21
|
+
"prettier": {
|
|
22
|
+
"singleQuote": true,
|
|
23
|
+
"printWidth": 120
|
|
24
|
+
},
|
|
20
25
|
"repository": {
|
|
21
26
|
"type": "git",
|
|
22
27
|
"url": "git+https://github.com/bringg/node-arnavmq.git"
|
|
@@ -37,12 +42,14 @@
|
|
|
37
42
|
"child-process-promise": "^2.2.1",
|
|
38
43
|
"eslint": "^8.25.0",
|
|
39
44
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
45
|
+
"eslint-config-prettier": "^8.6.0",
|
|
40
46
|
"eslint-plugin-import": "^2.26.0",
|
|
41
47
|
"mocha": "^10.0.0",
|
|
42
48
|
"nyc": "^15.1.0",
|
|
49
|
+
"prettier": "^2.8.3",
|
|
43
50
|
"sinon": "^14.0.1"
|
|
44
51
|
},
|
|
45
52
|
"engines": {
|
|
46
|
-
"node": ">=
|
|
53
|
+
"node": ">=14"
|
|
47
54
|
}
|
|
48
55
|
}
|
package/src/index.js
CHANGED
|
@@ -42,11 +42,14 @@ module.exports = (config) => {
|
|
|
42
42
|
*/
|
|
43
43
|
logger: utils.emptyLogger,
|
|
44
44
|
|
|
45
|
-
...config
|
|
45
|
+
...config,
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
if (configuration.transport !== utils.emptyLogger) {
|
|
49
|
-
process.emitWarning(
|
|
49
|
+
process.emitWarning(
|
|
50
|
+
"The 'transport' configuration option is deprecated. Please use the 'logger' option instead.",
|
|
51
|
+
'DeprecationWarning'
|
|
52
|
+
);
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
configuration.prefetch = parseInt(configuration.prefetch, 10) || 0;
|
package/src/modules/arnavmq.js
CHANGED
|
@@ -51,12 +51,12 @@ module.exports = (connection) => {
|
|
|
51
51
|
|
|
52
52
|
const consumer = {
|
|
53
53
|
consume: instance.consume.bind(instance),
|
|
54
|
-
subscribe: instance.subscribe.bind(instance)
|
|
54
|
+
subscribe: instance.subscribe.bind(instance),
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
const producer = {
|
|
58
58
|
produce: instance.produce.bind(instance),
|
|
59
|
-
publish: instance.publish.bind(instance)
|
|
59
|
+
publish: instance.publish.bind(instance),
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
return {
|
|
@@ -66,6 +66,6 @@ module.exports = (connection) => {
|
|
|
66
66
|
produce: producer.produce,
|
|
67
67
|
publish: producer.publish,
|
|
68
68
|
consumer,
|
|
69
|
-
producer
|
|
69
|
+
producer,
|
|
70
70
|
};
|
|
71
71
|
};
|
|
@@ -12,7 +12,7 @@ class Connection {
|
|
|
12
12
|
/**
|
|
13
13
|
* Connect to the broker. We keep only 1 connection for each connection string provided in config, as advised by RabbitMQ
|
|
14
14
|
* @return {Promise} A promise that resolve with an amqp.node connection object
|
|
15
|
-
|
|
15
|
+
*/
|
|
16
16
|
getConnection() {
|
|
17
17
|
const url = this._config.host;
|
|
18
18
|
const { hostname } = this._config;
|
|
@@ -25,28 +25,31 @@ class Connection {
|
|
|
25
25
|
// prepare the connection internal object, and reset channel if connection has been closed
|
|
26
26
|
this.connections[url] = {
|
|
27
27
|
conn: null,
|
|
28
|
-
channel: null
|
|
28
|
+
channel: null,
|
|
29
29
|
};
|
|
30
30
|
connection = this.connections[url];
|
|
31
|
-
connection.conn = amqp
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
delete connection
|
|
31
|
+
connection.conn = amqp
|
|
32
|
+
.connect(url, {
|
|
33
|
+
clientProperties: {
|
|
34
|
+
hostname,
|
|
35
|
+
arnavmq: packageVersion,
|
|
36
|
+
startedAt: this.startedAt,
|
|
37
|
+
connectedAt: new Date().toISOString(),
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
.then((conn) => {
|
|
41
|
+
// on connection close, delete connection
|
|
42
|
+
conn.on('close', () => {
|
|
43
|
+
delete connection.conn;
|
|
44
|
+
});
|
|
45
|
+
conn.on('error', this._onError.bind(this));
|
|
46
|
+
connection.conn = conn;
|
|
47
|
+
return conn;
|
|
48
|
+
})
|
|
49
|
+
.catch((e) => {
|
|
50
|
+
connection.conn = null;
|
|
51
|
+
throw e;
|
|
42
52
|
});
|
|
43
|
-
conn.on('error', this._onError.bind(this));
|
|
44
|
-
connection.conn = conn;
|
|
45
|
-
return conn;
|
|
46
|
-
}).catch((e) => {
|
|
47
|
-
connection.conn = null;
|
|
48
|
-
throw e;
|
|
49
|
-
});
|
|
50
53
|
return connection.conn;
|
|
51
54
|
}
|
|
52
55
|
|
|
@@ -54,7 +57,7 @@ class Connection {
|
|
|
54
57
|
* Create the channel on the broker, once connection is successfuly opened.
|
|
55
58
|
* Since RabbitMQ advise to open one channel by process and node is mono-core, we keep only 1 channel for the whole connection.
|
|
56
59
|
* @return {Promise} A promise that resolve with an amqp.node channel object
|
|
57
|
-
|
|
60
|
+
*/
|
|
58
61
|
getChannel() {
|
|
59
62
|
const url = this._config.host;
|
|
60
63
|
const { prefetch } = this._config;
|
|
@@ -65,17 +68,18 @@ class Connection {
|
|
|
65
68
|
return Promise.resolve(connection.chann);
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
connection.chann = connection.conn.createChannel()
|
|
69
|
-
.
|
|
70
|
-
channel.prefetch(prefetch);
|
|
71
|
+
connection.chann = connection.conn.createChannel().then((channel) => {
|
|
72
|
+
channel.prefetch(prefetch);
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
connection.chann = channel;
|
|
77
|
-
return channel;
|
|
74
|
+
// on error we remove the channel so the next call will recreate it (auto-reconnect are handled by connection users)
|
|
75
|
+
channel.on('close', () => {
|
|
76
|
+
delete connection.chann;
|
|
78
77
|
});
|
|
78
|
+
channel.on('error', this._onError.bind(this));
|
|
79
|
+
|
|
80
|
+
connection.chann = channel;
|
|
81
|
+
return channel;
|
|
82
|
+
});
|
|
79
83
|
return connection.chann;
|
|
80
84
|
}
|
|
81
85
|
|
|
@@ -87,7 +91,7 @@ class Connection {
|
|
|
87
91
|
this._config.transport.error(error);
|
|
88
92
|
this._config.logger.error({
|
|
89
93
|
message: error.message,
|
|
90
|
-
error
|
|
94
|
+
error,
|
|
91
95
|
});
|
|
92
96
|
}
|
|
93
97
|
|
package/src/modules/consumer.js
CHANGED
|
@@ -35,7 +35,7 @@ class Consumer {
|
|
|
35
35
|
this._connection.config.transport.debug(loggerAlias, `[${queue}][${msg.properties.replyTo}] >`, content);
|
|
36
36
|
this._connection.config.logger.debug({
|
|
37
37
|
message: `${loggerAlias} [${queue}][${msg.properties.replyTo}] > ${content}`,
|
|
38
|
-
params: { content }
|
|
38
|
+
params: { content },
|
|
39
39
|
});
|
|
40
40
|
this.channel.sendToQueue(msg.properties.replyTo, parsers.out(content, options), options);
|
|
41
41
|
}
|
|
@@ -68,55 +68,63 @@ class Consumer {
|
|
|
68
68
|
// ex: service-something with suffix :ci becomes service-suffix:ci etc.
|
|
69
69
|
const suffixedQueue = `${queue}${this._connection.config.consumerSuffix || ''}`;
|
|
70
70
|
|
|
71
|
-
return this._connection
|
|
72
|
-
|
|
71
|
+
return this._connection
|
|
72
|
+
.get()
|
|
73
|
+
.then((channel) => {
|
|
74
|
+
this.channel = channel;
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
return this.channel.assertQueue(suffixedQueue, options).then((q) => {
|
|
80
|
-
this._connection.config.transport.debug(loggerAlias, 'init', q.queue);
|
|
81
|
-
this._connection.config.logger.debug({
|
|
82
|
-
message: `${loggerAlias} init ${q.queue}`,
|
|
83
|
-
params: { queue: q.queue }
|
|
76
|
+
// when channel is closed, we want to be sure we recreate the queue ASAP so we trigger a reconnect by recreating the consumer
|
|
77
|
+
this.channel.addListener('close', () => {
|
|
78
|
+
this.subscribe(queue, options, callback);
|
|
84
79
|
});
|
|
85
80
|
|
|
86
|
-
this.channel.
|
|
87
|
-
|
|
88
|
-
this._connection.config.transport.debug(loggerAlias, `[${q.queue}] < ${messageString}`);
|
|
81
|
+
return this.channel.assertQueue(suffixedQueue, options).then((q) => {
|
|
82
|
+
this._connection.config.transport.debug(loggerAlias, 'init', q.queue);
|
|
89
83
|
this._connection.config.logger.debug({
|
|
90
|
-
message: `${loggerAlias}
|
|
91
|
-
params: { queue: q.queue
|
|
84
|
+
message: `${loggerAlias} init ${q.queue}`,
|
|
85
|
+
params: { queue: q.queue },
|
|
92
86
|
});
|
|
93
87
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
.catch((error) => {
|
|
103
|
-
// if something bad happened in the callback, reject the message so we can requeue it (or not)
|
|
104
|
-
this._connection.config.transport.error(loggerAlias, error);
|
|
105
|
-
this._connection.config.logger.error({
|
|
106
|
-
message: `${loggerAlias} Failed processing message from queue ${q.queue}: ${error.message}`,
|
|
107
|
-
error,
|
|
108
|
-
params: { queue: q.queue, message: messageString }
|
|
88
|
+
this.channel.consume(
|
|
89
|
+
q.queue,
|
|
90
|
+
(msg) => {
|
|
91
|
+
const messageString = msg.content.toString();
|
|
92
|
+
this._connection.config.transport.debug(loggerAlias, `[${q.queue}] < ${messageString}`);
|
|
93
|
+
this._connection.config.logger.debug({
|
|
94
|
+
message: `${loggerAlias} [${q.queue}] < ${messageString}`,
|
|
95
|
+
params: { queue: q.queue, message: messageString },
|
|
109
96
|
});
|
|
110
97
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
98
|
+
// main answer management chaining
|
|
99
|
+
// receive message, parse it, execute callback, check if should answer, ack/reject message
|
|
100
|
+
Promise.resolve(parsers.in(msg))
|
|
101
|
+
.then((body) => callback(body, msg.properties))
|
|
102
|
+
.then(this.checkRpc(msg, q.queue))
|
|
103
|
+
.then(() => {
|
|
104
|
+
this.channel.ack(msg);
|
|
105
|
+
})
|
|
106
|
+
.catch((error) => {
|
|
107
|
+
// if something bad happened in the callback, reject the message so we can requeue it (or not)
|
|
108
|
+
this._connection.config.transport.error(loggerAlias, error);
|
|
109
|
+
this._connection.config.logger.error({
|
|
110
|
+
message: `${loggerAlias} Failed processing message from queue ${q.queue}: ${error.message}`,
|
|
111
|
+
error,
|
|
112
|
+
params: { queue: q.queue, message: messageString },
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
this.channel.reject(msg, this._connection.config.requeue);
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
{ noAck: false }
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
// in case of any error creating the channel, wait for some time and then try to reconnect again (to avoid overflow)
|
|
124
|
+
})
|
|
125
|
+
.catch(() =>
|
|
126
|
+
utils.timeoutPromise(this._connection.config.timeout).then(() => this.subscribe(queue, options, callback))
|
|
127
|
+
);
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
|
package/src/modules/producer.js
CHANGED
|
@@ -50,27 +50,21 @@ class Producer {
|
|
|
50
50
|
|
|
51
51
|
if (responsePromise === undefined) {
|
|
52
52
|
const error = new Error(`Receiving RPC message from previous session: callback no more in memory. ${queue}`);
|
|
53
|
-
this._connection.config.transport.warn(
|
|
54
|
-
loggerAlias,
|
|
55
|
-
error
|
|
56
|
-
);
|
|
53
|
+
this._connection.config.transport.warn(loggerAlias, error);
|
|
57
54
|
this._connection.config.logger.warn({
|
|
58
55
|
message: `${loggerAlias} ${error.message}`,
|
|
59
56
|
error,
|
|
60
|
-
params: { queue, rpcQueue }
|
|
57
|
+
params: { queue, rpcQueue },
|
|
61
58
|
});
|
|
62
59
|
|
|
63
60
|
return;
|
|
64
61
|
}
|
|
65
62
|
|
|
66
63
|
// if we found one, we execute the callback and delete it because it will never be received again anyway
|
|
67
|
-
this._connection.config.transport.info(
|
|
68
|
-
loggerAlias,
|
|
69
|
-
`[${queue}] < answer`
|
|
70
|
-
);
|
|
64
|
+
this._connection.config.transport.info(loggerAlias, `[${queue}] < answer`);
|
|
71
65
|
this._connection.config.logger.debug({
|
|
72
66
|
message: `${loggerAlias} [${queue}] < answer`,
|
|
73
|
-
params: { queue }
|
|
67
|
+
params: { queue },
|
|
74
68
|
});
|
|
75
69
|
|
|
76
70
|
try {
|
|
@@ -100,30 +94,30 @@ class Producer {
|
|
|
100
94
|
const resQueue = `${queue}:${this._connection.config.hostname}:${process.pid}:res`;
|
|
101
95
|
rpcQueue.queue = this._connection
|
|
102
96
|
.get()
|
|
103
|
-
.then((channel) =>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
97
|
+
.then((channel) =>
|
|
98
|
+
channel
|
|
99
|
+
.assertQueue(resQueue, {
|
|
100
|
+
durable: true,
|
|
101
|
+
exclusive: true,
|
|
102
|
+
})
|
|
103
|
+
.then((q) => {
|
|
104
|
+
rpcQueue.queue = q.queue;
|
|
105
|
+
|
|
106
|
+
// if channel is closed, we want to make sure we cleanup the queue so future calls will recreate it
|
|
107
|
+
this._connection.addListener('close', () => {
|
|
108
|
+
delete rpcQueue.queue;
|
|
109
|
+
this.createRpcQueue(queue);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return channel.consume(q.queue, this.maybeAnswer(queue), {
|
|
113
|
+
noAck: true,
|
|
114
|
+
});
|
|
115
|
+
})
|
|
116
|
+
.then(() => rpcQueue.queue)
|
|
117
|
+
)
|
|
122
118
|
.catch(() => {
|
|
123
119
|
delete rpcQueue.queue;
|
|
124
|
-
return utils
|
|
125
|
-
.timeoutPromise(this._connection.config.timeout)
|
|
126
|
-
.then(() => this.createRpcQueue(queue));
|
|
120
|
+
return utils.timeoutPromise(this._connection.config.timeout).then(() => this.createRpcQueue(queue));
|
|
127
121
|
});
|
|
128
122
|
|
|
129
123
|
return rpcQueue.queue;
|
|
@@ -236,14 +230,10 @@ class Producer {
|
|
|
236
230
|
// undefined can't be serialized/buffered :p
|
|
237
231
|
if (!message) message = null;
|
|
238
232
|
|
|
239
|
-
this._connection.config.transport.info(
|
|
240
|
-
loggerAlias,
|
|
241
|
-
`[${queue}] > `,
|
|
242
|
-
message
|
|
243
|
-
);
|
|
233
|
+
this._connection.config.transport.info(loggerAlias, `[${queue}] > `, message);
|
|
244
234
|
this._connection.config.logger.debug({
|
|
245
235
|
message: `${loggerAlias} [${queue}] > ${message}`,
|
|
246
|
-
params: { queue, message }
|
|
236
|
+
params: { queue, message },
|
|
247
237
|
});
|
|
248
238
|
|
|
249
239
|
return this.checkRpc(queue, parsers.out(message, settings), settings);
|
|
@@ -258,7 +248,7 @@ class Producer {
|
|
|
258
248
|
this._connection.config.logger.error({
|
|
259
249
|
message: `${loggerAlias} Failed sending message to queue ${queue}: ${error.message}`,
|
|
260
250
|
error,
|
|
261
|
-
params: { queue, message }
|
|
251
|
+
params: { queue, message },
|
|
262
252
|
});
|
|
263
253
|
return utils
|
|
264
254
|
.timeoutPromise(this._connection.config.timeout)
|
package/src/modules/utils.js
CHANGED
|
@@ -5,7 +5,7 @@ const emptyLogger = {
|
|
|
5
5
|
debug: empty,
|
|
6
6
|
warn: empty,
|
|
7
7
|
error: empty,
|
|
8
|
-
log: empty
|
|
8
|
+
log: empty,
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
module.exports = {
|
|
@@ -26,7 +26,8 @@ module.exports = {
|
|
|
26
26
|
* @param {number} timer How much ws to wait
|
|
27
27
|
* @return {Promise} A Promise that will resolve when timer is expired
|
|
28
28
|
*/
|
|
29
|
-
timeoutPromise: (timer) =>
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
timeoutPromise: (timer) =>
|
|
30
|
+
new Promise((resolve) => {
|
|
31
|
+
setTimeout(resolve, timer);
|
|
32
|
+
}),
|
|
32
33
|
};
|