rascal 18.0.1 → 20.0.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/CHANGELOG.md +10 -0
- package/README.md +9 -7
- package/lib/amqp/Publication.js +37 -19
- package/lib/amqp/Subscription.js +50 -35
- package/lib/amqp/Vhost.js +34 -12
- package/lib/config/configure.js +2 -1
- package/lib/management/Client.js +21 -20
- package/package.json +1 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 20.0.0
|
|
4
|
+
- Replaced superagent with native node http client as per https://github.com/onebeyond/rascal/issues/234
|
|
5
|
+
|
|
6
|
+
## 19.0.0
|
|
7
|
+
- I am not aware of any breaking changes in this release, but emitting error events asynchronously could have subtle side effects, hence the major release
|
|
8
|
+
- Deprecate session 'cancelled' event in favour of 'cancel' (both will work)
|
|
9
|
+
- Refactor reconnection and resubscription code
|
|
10
|
+
- Emit errors asynchronously to prevent them being caught by the amqplib main accept loop
|
|
11
|
+
- Fix bug which throw an exception in the error handler when a close event was emitted with no error argument
|
|
12
|
+
|
|
3
13
|
## 18.0.1
|
|
4
14
|
|
|
5
15
|
- Removed console.log when the channel pool destroyed a channel
|
package/README.md
CHANGED
|
@@ -130,7 +130,9 @@ The reason Rascal nacks the message is because the alternatives are to leave the
|
|
|
130
130
|
|
|
131
131
|
## Very Important Section About Event Handling
|
|
132
132
|
|
|
133
|
-
[amqplib](https://www.npmjs.com/package/amqplib) emits error events when a connection or channel encounters a problem. Rascal will listen for these and provided you use the default configuration will attempt automatic recovery (reconnection etc), however these events can indicate errors in your code, so it's also important to bring them to your attention. Rascal does this by re-emitting the error event, which means if you don't handle them, they will bubble up to the uncaught error handler and crash your application.
|
|
133
|
+
[amqplib](https://www.npmjs.com/package/amqplib) emits error events when a connection or channel encounters a problem. Rascal will listen for these and provided you use the default configuration will attempt automatic recovery (reconnection etc), however these events can indicate errors in your code, so it's also important to bring them to your attention. Rascal does this by re-emitting the error event, which means if you don't handle them, they will bubble up to the uncaught error handler and crash your application. It is insufficient to register a global uncaughtException handler - doing so without registering individual handlers will prevent your application from crashing, but also prevent Rascal from recovering.
|
|
134
|
+
|
|
135
|
+
There are four places where you need to register error handlers.
|
|
134
136
|
|
|
135
137
|
1. Immediately after obtaining a broker instance
|
|
136
138
|
|
|
@@ -462,7 +464,7 @@ The AMQP protocol doesn't support assertion or checking of vhosts, so Rascal use
|
|
|
462
464
|
}
|
|
463
465
|
```
|
|
464
466
|
|
|
465
|
-
Rascal uses [
|
|
467
|
+
Rascal uses [http.request](https://nodejs.org/api/http.html#httprequesturl-options-callback) under the hood. URL configuration is also supported.
|
|
466
468
|
|
|
467
469
|
```json
|
|
468
470
|
{
|
|
@@ -487,11 +489,11 @@ Rascal uses [superagent](https://github.com/visionmedia/superagent) under the ho
|
|
|
487
489
|
}
|
|
488
490
|
```
|
|
489
491
|
|
|
490
|
-
You can also supply your own agent via the broker components. Use this when you need to set [TLS options](https://
|
|
492
|
+
You can also supply your own agent via the broker components. Use this when you need to set [TLS options](https://nodejs.org/api/https.html#httpsrequesturl-options-callback).
|
|
491
493
|
|
|
492
494
|
```js
|
|
493
|
-
const
|
|
494
|
-
const agent =
|
|
495
|
+
const https = require('https');
|
|
496
|
+
const agent = new https.Agent(options);
|
|
495
497
|
const components = { agent };
|
|
496
498
|
const broker = await Broker.create(config, components);
|
|
497
499
|
```
|
|
@@ -805,7 +807,7 @@ You also need to think about how you will [track the consumer offset](https://ww
|
|
|
805
807
|
});
|
|
806
808
|
```
|
|
807
809
|
|
|
808
|
-
However, if your application is offline for too long, and messages are still being published to the stream, it may not be able to resume from where you left off, since those messages may have been deleted.
|
|
810
|
+
However, if your application is offline for too long, and messages are still being published to the stream, it may not be able to resume from where you left off, since those messages may have been deleted. Furthermore, if your application consumes messages concurrently, you need to think about how you will recover should one fail. If you naively override the previouly saved offset, you may be replacing a higher/later offset with an lower/older one, causing in your application to restart from the wrong point. Finally, you also need to decide what to do if the message cannot be processed. You cannot simply replay the message since you are working with a stream, rather than a queue. You could cancel the subscription and resume from the current offset, but this will lead to duplicates if you have been consuming messages concurrently. Alternatively you could republish the failures to a dead letter queue and process them separately.
|
|
809
811
|
|
|
810
812
|
For the above reasons, we only recommend considering streams when you genuinely need the extra throughput.
|
|
811
813
|
|
|
@@ -1358,7 +1360,7 @@ If the message has not been auto-acknowledged you should ackOrNack it. **If you
|
|
|
1358
1360
|
|
|
1359
1361
|
The RabbitMQ broker may [cancel](https://www.rabbitmq.com/consumer-cancel.html) the consumer if the queue is deleted or the node on which the queue is located fails. [amqplib](https://www.squaremobius.net/amqp.node/channel_api.html#channel_consume) handles this by delivering a `null` message. When Rascal receives the null message it will
|
|
1360
1362
|
|
|
1361
|
-
1. Emit a `
|
|
1363
|
+
1. Emit a `cancel` event from the subscription.
|
|
1362
1364
|
1. Emit an `error` event from the subscription if the `cancel` event was not handled
|
|
1363
1365
|
1. Optionally attempt to resubscribe as per normal retry configuration. If the queue was deleted rather than being failed over, the queue will not automatically be re-created and retry attempts will fail indefinitely.
|
|
1364
1366
|
|
package/lib/amqp/Publication.js
CHANGED
|
@@ -95,24 +95,27 @@ function Publication(vhost, borrowChannelFn, returnChannelFn, destroyChannelFn,
|
|
|
95
95
|
session._removePausedListener();
|
|
96
96
|
if (err) return session.emit('error', err, messageId);
|
|
97
97
|
if (session.isAborted()) return abortPublish(channel, messageId);
|
|
98
|
-
|
|
98
|
+
|
|
99
|
+
const disconnectionHandler = makeDisconnectionHandler(channel, messageId, session, config);
|
|
99
100
|
const returnHandler = session.emit.bind(session, 'return');
|
|
100
|
-
addListeners(channel,
|
|
101
|
+
addListeners(channel, disconnectionHandler, returnHandler);
|
|
102
|
+
|
|
101
103
|
try {
|
|
102
104
|
session._startPublish();
|
|
105
|
+
|
|
103
106
|
publishFn(channel, buffer, publishConfig, (err, ok) => {
|
|
104
107
|
session._endPublish();
|
|
105
108
|
if (err) {
|
|
106
|
-
destroyChannel(channel,
|
|
109
|
+
destroyChannel(channel, disconnectionHandler, returnHandler);
|
|
107
110
|
return session.emit('error', err, messageId);
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
ok ? returnChannel(channel,
|
|
113
|
+
ok ? returnChannel(channel, disconnectionHandler, returnHandler) : deferReturnChannel(channel, disconnectionHandler, returnHandler);
|
|
111
114
|
|
|
112
115
|
session.emit('success', messageId);
|
|
113
116
|
});
|
|
114
117
|
} catch (err) {
|
|
115
|
-
returnChannel(channel,
|
|
118
|
+
returnChannel(channel, disconnectionHandler, returnHandler);
|
|
116
119
|
return session.emit('error', err, messageId);
|
|
117
120
|
}
|
|
118
121
|
});
|
|
@@ -125,19 +128,19 @@ function Publication(vhost, borrowChannelFn, returnChannelFn, destroyChannelFn,
|
|
|
125
128
|
returnChannelFn(channel);
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
function returnChannel(channel,
|
|
129
|
-
removeListeners(channel,
|
|
131
|
+
function returnChannel(channel, disconnectionHandler, returnHandler) {
|
|
132
|
+
removeListeners(channel, disconnectionHandler, returnHandler);
|
|
130
133
|
returnChannelFn(channel);
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
function deferReturnChannel(channel,
|
|
136
|
+
function deferReturnChannel(channel, disconnectionHandler, returnHandler) {
|
|
134
137
|
channel.once('drain', () => {
|
|
135
|
-
returnChannel(channel,
|
|
138
|
+
returnChannel(channel, disconnectionHandler, returnHandler);
|
|
136
139
|
});
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
function destroyChannel(channel,
|
|
140
|
-
removeListeners(channel,
|
|
142
|
+
function destroyChannel(channel, disconnectionHandler, returnHandler) {
|
|
143
|
+
removeListeners(channel, disconnectionHandler, returnHandler);
|
|
141
144
|
destroyChannelFn(channel);
|
|
142
145
|
}
|
|
143
146
|
|
|
@@ -163,19 +166,29 @@ function Publication(vhost, borrowChannelFn, returnChannelFn, destroyChannelFn,
|
|
|
163
166
|
}
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
function
|
|
167
|
-
|
|
169
|
+
function makeDisconnectionHandler(channel, messageId, session, config) {
|
|
170
|
+
return _.once((err) => {
|
|
171
|
+
// Use setImmediate to avoid amqplib accept loop swallowing errors
|
|
172
|
+
setImmediate(() => (err
|
|
173
|
+
// Treat close events with errors as error events
|
|
174
|
+
? handleChannelError(channel, messageId, session, config, err)
|
|
175
|
+
: handleChannelClose(channel, messageId, session, config)));
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function addListeners(channel, disconnectionHandler, returnHandler) {
|
|
180
|
+
channel.on('error', disconnectionHandler);
|
|
168
181
|
channel.on('return', returnHandler);
|
|
169
|
-
channel.connection.once('error',
|
|
170
|
-
channel.connection.once('close',
|
|
182
|
+
channel.connection.once('error', disconnectionHandler);
|
|
183
|
+
channel.connection.once('close', disconnectionHandler);
|
|
171
184
|
}
|
|
172
185
|
|
|
173
|
-
function removeListeners(channel,
|
|
186
|
+
function removeListeners(channel, disconnectionHandler, returnHandler) {
|
|
174
187
|
channel.removeAllListeners('drain');
|
|
175
|
-
channel.removeListener('error',
|
|
188
|
+
channel.removeListener('error', disconnectionHandler);
|
|
176
189
|
channel.removeListener('return', returnHandler);
|
|
177
|
-
channel.connection.removeListener('error',
|
|
178
|
-
channel.connection.removeListener('close',
|
|
190
|
+
channel.connection.removeListener('error', disconnectionHandler);
|
|
191
|
+
channel.connection.removeListener('close', disconnectionHandler);
|
|
179
192
|
}
|
|
180
193
|
|
|
181
194
|
function publishToExchange(channel, content, config, next) {
|
|
@@ -252,3 +265,8 @@ function handleChannelError(borked, messageId, emitter, config, err) {
|
|
|
252
265
|
debug('Channel error: %s during publication of message: %s to %s using channel: %s', err.message, messageId, config.name, borked._rascal_id);
|
|
253
266
|
emitter.emit('error', err, messageId);
|
|
254
267
|
}
|
|
268
|
+
|
|
269
|
+
function handleChannelClose(borked, messageId, emitter, config) {
|
|
270
|
+
debug('Channel closed during publication of message: %s to %s using channel: %s', messageId, config.name, borked._rascal_id);
|
|
271
|
+
emitter.emit('close', messageId);
|
|
272
|
+
}
|
package/lib/amqp/Subscription.js
CHANGED
|
@@ -61,13 +61,13 @@ function Subscription(broker, vhost, subscriptionConfig, counter) {
|
|
|
61
61
|
_configureQos(config, channel, (err) => {
|
|
62
62
|
if (err) return done(err);
|
|
63
63
|
|
|
64
|
-
const
|
|
65
|
-
const onMessage = _onMessage.bind(null, session, config,
|
|
64
|
+
const removeDisconnectionHandlers = attachDisconnectionHandlers(channel, session, config);
|
|
65
|
+
const onMessage = _onMessage.bind(null, session, config, removeDisconnectionHandlers);
|
|
66
66
|
|
|
67
67
|
channel.consume(config.source, onMessage, config.options, (err, response) => {
|
|
68
68
|
if (err) {
|
|
69
69
|
debug('Error subscribing to %s using channel: %s. %s', config.source, channel._rascal_id, err.message);
|
|
70
|
-
|
|
70
|
+
removeDisconnectionHandlers();
|
|
71
71
|
return done(err);
|
|
72
72
|
}
|
|
73
73
|
session._open(channel, response.consumerTag, (err) => {
|
|
@@ -88,8 +88,8 @@ function Subscription(broker, vhost, subscriptionConfig, counter) {
|
|
|
88
88
|
async.series(qos, next);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
function _onMessage(session, config,
|
|
92
|
-
if (!message) return handleConsumerCancel(session, config,
|
|
91
|
+
function _onMessage(session, config, removeDisconnectionHandlers, message) {
|
|
92
|
+
if (!message) return handleConsumerCancel(session, config, removeDisconnectionHandlers);
|
|
93
93
|
|
|
94
94
|
debug('Received message: %s from queue: %s', message.properties.messageId, config.queue);
|
|
95
95
|
session._incrementUnacknowledgeMessageCount(message.fields.consumerTag);
|
|
@@ -249,51 +249,66 @@ function Subscription(broker, vhost, subscriptionConfig, counter) {
|
|
|
249
249
|
if (err) session.emit('error', err);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
function
|
|
252
|
+
function attachDisconnectionHandlers(channel, session, config) {
|
|
253
253
|
/* eslint-disable no-use-before-define */
|
|
254
254
|
const connection = channel.connection;
|
|
255
|
-
const
|
|
256
|
-
channel.removeListener('error',
|
|
255
|
+
const removeDisconnectionHandlers = _.once(() => {
|
|
256
|
+
channel.removeListener('error', disconnectionHandler);
|
|
257
257
|
channel.on('error', (err) => {
|
|
258
258
|
debug('Suppressing error on cancelled session: %s to prevent connection errors. %s', channel._rascal_id, err.message);
|
|
259
259
|
});
|
|
260
|
-
connection.removeListener('error',
|
|
261
|
-
connection.removeListener('close',
|
|
260
|
+
connection.removeListener('error', disconnectionHandler);
|
|
261
|
+
connection.removeListener('close', disconnectionHandler);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const disconnectionHandler = makeDisconnectionHandler(session, config, removeDisconnectionHandlers);
|
|
265
|
+
channel.on('error', disconnectionHandler);
|
|
266
|
+
connection.once('error', disconnectionHandler);
|
|
267
|
+
connection.once('close', disconnectionHandler);
|
|
268
|
+
return removeDisconnectionHandlers;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function makeDisconnectionHandler(session, config, removeDisconnectionHandlers) {
|
|
272
|
+
return _.once((err) => {
|
|
273
|
+
// Use setImmediate to avoid amqplib accept loop swallowing errors
|
|
274
|
+
setImmediate(() => (err
|
|
275
|
+
// Treat close events with errors as error events
|
|
276
|
+
? handleChannelError(session, config, removeDisconnectionHandlers, 0, err)
|
|
277
|
+
: handleChannelClose(session, config, removeDisconnectionHandlers, 0)));
|
|
262
278
|
});
|
|
263
|
-
const errorHandler = _.once(handleChannelError.bind(null, session, config, removeErrorHandlers, 0));
|
|
264
|
-
channel.on('error', errorHandler);
|
|
265
|
-
connection.once('error', errorHandler);
|
|
266
|
-
connection.once('close', errorHandler);
|
|
267
|
-
return removeErrorHandlers;
|
|
268
279
|
}
|
|
269
280
|
|
|
270
|
-
function handleChannelError(session, config,
|
|
281
|
+
function handleChannelError(session, config, removeDisconnectionHandler, attempt, err) {
|
|
271
282
|
debug('Handling channel error: %s from %s using channel: %s', err.message, config.name, session._getRascalChannelId());
|
|
272
|
-
if (
|
|
283
|
+
if (removeDisconnectionHandler) removeDisconnectionHandler();
|
|
273
284
|
session.emit('error', err);
|
|
274
|
-
config
|
|
275
|
-
&& subscribeNow(session, config, (err) => {
|
|
276
|
-
if (!err) return;
|
|
277
|
-
const delay = timer.next();
|
|
278
|
-
debug('Will attempt resubscription(%d) to %s in %dms', attempts + 1, config.name, delay);
|
|
279
|
-
session._schedule(handleChannelError.bind(null, session, config, null, attempts + 1, err), delay);
|
|
280
|
-
});
|
|
285
|
+
retrySubscription(session, config, attempt + 1);
|
|
281
286
|
}
|
|
282
287
|
|
|
283
|
-
function
|
|
288
|
+
function handleChannelClose(session, config, removeDisconnectionHandler, attempt) {
|
|
289
|
+
debug('Handling channel close from %s using channel: %s', config.name, session._getRascalChannelId());
|
|
290
|
+
removeDisconnectionHandler();
|
|
291
|
+
session.emit('close');
|
|
292
|
+
retrySubscription(session, config, attempt + 1);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function handleConsumerCancel(session, config, removeDisconnectionHandler) {
|
|
284
296
|
debug('Received consumer cancel from %s using channel: %s', config.name, session._getRascalChannelId());
|
|
285
|
-
|
|
297
|
+
removeDisconnectionHandler();
|
|
298
|
+
const cancelErr = new Error(format('Subscription: %s was cancelled by the broker', config.name));
|
|
299
|
+
session.emit('cancelled', cancelErr) || session.emit('cancel', cancelErr) || session.emit('error', cancelErr);
|
|
286
300
|
session._close((err) => {
|
|
287
301
|
if (err) debug('Error cancelling subscription: %s', err.message);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
302
|
+
retrySubscription(session, config, 1);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function retrySubscription(session, config, attempt) {
|
|
307
|
+
config.retry && subscribeNow(session, config, (err) => {
|
|
308
|
+
if (!err) return;
|
|
309
|
+
const delay = timer.next();
|
|
310
|
+
debug('Will attempt resubscription(%d) to %s in %dms', attempt, config.name, delay);
|
|
311
|
+
session._schedule(handleChannelError.bind(null, session, config, null, attempt, err), delay);
|
|
297
312
|
});
|
|
298
313
|
}
|
|
299
314
|
}
|
package/lib/amqp/Vhost.js
CHANGED
|
@@ -57,7 +57,7 @@ function Vhost(vhostConfig, components) {
|
|
|
57
57
|
connectionConfig = ctx.connectionConfig;
|
|
58
58
|
timer = backoff(ctx.connectionConfig.retry);
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
attachDisconnectionHandlers(config);
|
|
61
61
|
forwardRabbitMQConnectionEvents();
|
|
62
62
|
ensureChannelPools();
|
|
63
63
|
resumeChannelAllocation();
|
|
@@ -471,11 +471,21 @@ function Vhost(vhostConfig, components) {
|
|
|
471
471
|
);
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
function
|
|
474
|
+
function attachDisconnectionHandlers(config) {
|
|
475
475
|
connection.removeAllListeners('error');
|
|
476
|
-
const
|
|
477
|
-
connection.on('error',
|
|
478
|
-
connection.on('close',
|
|
476
|
+
const disconectionHandler = makeDisconnectionHandler(config);
|
|
477
|
+
connection.on('error', disconectionHandler);
|
|
478
|
+
connection.on('close', disconectionHandler);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function makeDisconnectionHandler(config) {
|
|
482
|
+
return _.once((err) => {
|
|
483
|
+
// Use setImmediate to avoid amqplib accept loop swallowing errors
|
|
484
|
+
setImmediate(() => (err
|
|
485
|
+
// Treat close events with errors as error events
|
|
486
|
+
? handleConnectionError(connection, config, err)
|
|
487
|
+
: handleConnectionClose(connection, config)));
|
|
488
|
+
});
|
|
479
489
|
}
|
|
480
490
|
|
|
481
491
|
function handleConnectionError(borked, config, err) {
|
|
@@ -484,12 +494,24 @@ function Vhost(vhostConfig, components) {
|
|
|
484
494
|
connection = undefined;
|
|
485
495
|
self.emit('disconnect');
|
|
486
496
|
self.emit('error', err, self.getConnectionDetails());
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
497
|
+
retryConnection(borked, config);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function handleConnectionClose(borked, config) {
|
|
501
|
+
debug('Handling connection close initially from connection: %s, %s', borked._rascal_id, connectionConfig.loggableUrl);
|
|
502
|
+
pauseChannelAllocation();
|
|
503
|
+
connection = undefined;
|
|
504
|
+
self.emit('disconnect');
|
|
505
|
+
self.emit('close', self.getConnectionDetails());
|
|
506
|
+
retryConnection(borked, config);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function retryConnection(borked, config) {
|
|
510
|
+
connectionConfig.retry && self.init((err) => {
|
|
511
|
+
if (!err) return;
|
|
512
|
+
const delay = timer.next();
|
|
513
|
+
debug('Will attempt reconnection in in %dms', delay);
|
|
514
|
+
reconnectTimeout = setTimeoutUnref(handleConnectionError.bind(null, borked, config, err), delay);
|
|
515
|
+
});
|
|
494
516
|
}
|
|
495
517
|
}
|
package/lib/config/configure.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
const debug = require('debug')('rascal:config:configure');
|
|
2
2
|
const format = require('util').format;
|
|
3
3
|
const url = require('url');
|
|
4
|
-
const { URL } = require('node:url');
|
|
5
4
|
const _ = require('lodash');
|
|
6
5
|
const uuid = require('uuid').v4;
|
|
7
6
|
const XRegExp = require('xregexp');
|
|
8
7
|
const baseline = require('./baseline');
|
|
9
8
|
const fqn = require('./fqn');
|
|
10
9
|
|
|
10
|
+
const { URL } = url;
|
|
11
|
+
|
|
11
12
|
module.exports = _.curry((rascalConfig, next) => {
|
|
12
13
|
rascalConfig = _.defaultsDeep(rascalConfig, baseline);
|
|
13
14
|
|
package/lib/management/Client.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
|
+
const http = require('http');
|
|
1
2
|
const debug = require('debug')('rascal:management:client');
|
|
2
3
|
const format = require('util').format;
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
const defaultAgent = require('superagent');
|
|
5
4
|
|
|
6
|
-
function Client(
|
|
7
|
-
const agent = suppliedAgent || defaultAgent;
|
|
5
|
+
function Client(agent) {
|
|
8
6
|
const self = this;
|
|
9
7
|
|
|
10
8
|
this.assertVhost = function (name, config, next) {
|
|
11
9
|
debug('Asserting vhost: %s', name);
|
|
12
|
-
const
|
|
13
|
-
self._request('
|
|
10
|
+
const url = getUrl(name, config);
|
|
11
|
+
self._request('PUT', url, config.options, (err) => {
|
|
14
12
|
if (!err) return next();
|
|
15
13
|
const _err = err.status ? new Error(format('Failed to assert vhost: %s. %s returned status %d', name, config.loggableUrl, err.status)) : err;
|
|
16
14
|
return next(_err);
|
|
@@ -19,8 +17,8 @@ function Client(suppliedAgent) {
|
|
|
19
17
|
|
|
20
18
|
this.checkVhost = function (name, config, next) {
|
|
21
19
|
debug('Checking vhost: %s', name);
|
|
22
|
-
const
|
|
23
|
-
self._request('
|
|
20
|
+
const url = getUrl(name, config);
|
|
21
|
+
self._request('GET', url, config.options, (err) => {
|
|
24
22
|
if (!err) return next();
|
|
25
23
|
const _err = err.status ? new Error(format('Failed to check vhost: %s. %s returned status %d', name, config.loggableUrl, err.status)) : err;
|
|
26
24
|
return next(_err);
|
|
@@ -29,26 +27,29 @@ function Client(suppliedAgent) {
|
|
|
29
27
|
|
|
30
28
|
this.deleteVhost = function (name, config, next) {
|
|
31
29
|
debug('Deleting vhost: %s', name);
|
|
32
|
-
const
|
|
33
|
-
self._request('
|
|
30
|
+
const url = getUrl(name, config);
|
|
31
|
+
self._request('DELETE', url, config.options, (err) => {
|
|
34
32
|
if (!err) return next();
|
|
35
33
|
const _err = err.status ? new Error(format('Failed to delete vhost: %s. %s returned status %d', name, config.loggableUrl, err.status)) : err;
|
|
36
34
|
return next(_err);
|
|
37
35
|
});
|
|
38
36
|
};
|
|
39
37
|
|
|
40
|
-
this._request = function (method, url,
|
|
41
|
-
agent
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
next();
|
|
45
|
-
}
|
|
46
|
-
.
|
|
38
|
+
this._request = function (method, url, options, next) {
|
|
39
|
+
const req = http.request(url, { ...options, method, agent }, (res) => {
|
|
40
|
+
if (res.statusCode >= 300) {
|
|
41
|
+
const err = Object.assign(new Error('HTTP Error'), { status: res.statusCode });
|
|
42
|
+
return next(err);
|
|
43
|
+
}
|
|
44
|
+
res.on('data', () => {});
|
|
45
|
+
res.on('end', () => next());
|
|
46
|
+
});
|
|
47
|
+
req.on('error', next);
|
|
48
|
+
req.end();
|
|
47
49
|
};
|
|
48
50
|
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
return _.defaultsDeep({ url }, config.options);
|
|
51
|
+
function getUrl(name, config) {
|
|
52
|
+
return format('%s/%s/%s', config.url, 'api/vhosts', name);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rascal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "20.0.0",
|
|
4
4
|
"description": "A config driven wrapper for amqplib supporting multi-host connections, automatic error recovery, redelivery flood protection, transparent encryption / decryption, channel pooling and publication timeouts",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"lru-cache": "^7.10.1",
|
|
13
13
|
"safe-json-parse": "^4.0.0",
|
|
14
14
|
"stashback": "^2.0.1",
|
|
15
|
-
"superagent": "^8.0.9",
|
|
16
15
|
"uuid": "^8.3.2",
|
|
17
16
|
"xregexp": "^5.1.0"
|
|
18
17
|
},
|
|
@@ -27,7 +26,6 @@
|
|
|
27
26
|
"lint-staged": "^11.2.4",
|
|
28
27
|
"nyc": "^15.1.0",
|
|
29
28
|
"random-readable": "^1.0.1",
|
|
30
|
-
"superagent-defaults": "^0.1.14",
|
|
31
29
|
"zunit": "^4.0.0"
|
|
32
30
|
},
|
|
33
31
|
"peerDependencies": {
|