@palmetto/pubsub 1.0.7 → 1.1.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/dist/rabbitmq/config.d.ts +10 -8
- package/dist/rabbitmq/config.js +29 -18
- package/dist/rabbitmq/connection.js +15 -11
- package/package.json +1 -1
- package/src/rabbitmq/README.md +20 -9
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AmqpConnectionManagerOptions } from "amqp-connection-manager";
|
|
1
2
|
import { MessageContext, PubSubConfiguration } from "../interfaces.js";
|
|
2
3
|
export interface RabbitMqConnectionConfig {
|
|
3
4
|
/**
|
|
@@ -12,13 +13,13 @@ export interface RabbitMqConnectionConfig {
|
|
|
12
13
|
* Timeout for publisher startup retries if the connection fails (default: 10 seconds)
|
|
13
14
|
*/
|
|
14
15
|
startupRetryTimeoutMs?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Override default amqp-connection-manager options (heartbeatIntervalInSeconds: 60, reconnectTimeInSeconds: 5)
|
|
18
|
+
*/
|
|
19
|
+
options?: AmqpConnectionManagerOptions;
|
|
15
20
|
}
|
|
16
21
|
export type QueueType = "default" | "dead-letter" | "retry";
|
|
17
|
-
export type ExchangeType = "direct" | "topic";
|
|
18
|
-
/**
|
|
19
|
-
* Default binding key for all messages
|
|
20
|
-
*/
|
|
21
|
-
export declare const DEFAULT_BINDING_KEY = "default";
|
|
22
|
+
export type ExchangeType = "direct" | "topic" | "fanout";
|
|
22
23
|
export interface RabbitQueueExchangeConfiguration extends PubSubConfiguration {
|
|
23
24
|
/**
|
|
24
25
|
* The queue name prefix
|
|
@@ -29,11 +30,11 @@ export interface RabbitQueueExchangeConfiguration extends PubSubConfiguration {
|
|
|
29
30
|
*/
|
|
30
31
|
topicSubscriberName?: string;
|
|
31
32
|
/**
|
|
32
|
-
* Support direct exchange where each message is delivered once, or topic exchange where each message is delivered to multiple queues
|
|
33
|
+
* Support direct exchange where each message is delivered once, or topic/fanout exchange where each message is delivered to multiple queues
|
|
33
34
|
*/
|
|
34
|
-
exchangeType
|
|
35
|
+
exchangeType: ExchangeType;
|
|
35
36
|
/**
|
|
36
|
-
* When true, the queue and exchange will be deleted after the consumers exit [note: dead-letter exchanges & queues
|
|
37
|
+
* When true, the queue and exchange will be deleted after the consumers exit [note: dead-letter exchanges & queues may remain when there are no dead-letter messages or consumers]
|
|
37
38
|
*/
|
|
38
39
|
temporary?: boolean;
|
|
39
40
|
/**
|
|
@@ -78,6 +79,7 @@ export interface RabbitMqMessageContext extends MessageContext {
|
|
|
78
79
|
* @returns The queue name
|
|
79
80
|
*/
|
|
80
81
|
export declare function getQueueName(config: RabbitQueueExchangeConfiguration): string;
|
|
82
|
+
export declare const ExchangeNameExtensions: Record<QueueType, string | undefined>;
|
|
81
83
|
export declare const QueueNameExtensions: Record<QueueType, string | undefined>;
|
|
82
84
|
/**
|
|
83
85
|
* Returns the exchange name based on the configuration
|
package/dist/rabbitmq/config.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QueueNameExtensions = exports.
|
|
3
|
+
exports.QueueNameExtensions = exports.ExchangeNameExtensions = void 0;
|
|
4
4
|
exports.getQueueName = getQueueName;
|
|
5
5
|
exports.getExchangeName = getExchangeName;
|
|
6
6
|
exports.getQueueType = getQueueType;
|
|
7
7
|
exports.getRoutingKey = getRoutingKey;
|
|
8
8
|
exports.getExchangeType = getExchangeType;
|
|
9
9
|
const errors_js_1 = require("../errors.js");
|
|
10
|
-
/**
|
|
11
|
-
* Default binding key for all messages
|
|
12
|
-
*/
|
|
13
|
-
exports.DEFAULT_BINDING_KEY = "default";
|
|
14
10
|
/**
|
|
15
11
|
* Returns the queue name based on the configuration
|
|
16
12
|
*
|
|
@@ -24,25 +20,28 @@ function getQueueName(config) {
|
|
|
24
20
|
return config.overrides.names[queueType].queueName;
|
|
25
21
|
}
|
|
26
22
|
let queueName = config.name;
|
|
27
|
-
if (config.exchangeType === "topic") {
|
|
23
|
+
if (config.exchangeType === "topic" || config.exchangeType === "fanout") {
|
|
28
24
|
if (!config.topicSubscriberName) {
|
|
29
|
-
throw new errors_js_1.ConfigurationError("Topic queues must have a subscriber name");
|
|
25
|
+
throw new errors_js_1.ConfigurationError("Topic/Fanout queues must have a subscriber name");
|
|
30
26
|
}
|
|
31
27
|
queueName += "." + config.topicSubscriberName;
|
|
32
28
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
else if (queueType === "retry") {
|
|
37
|
-
queueName += ".rtq";
|
|
29
|
+
const ext = exports.QueueNameExtensions[queueType];
|
|
30
|
+
if (ext) {
|
|
31
|
+
queueName += ext;
|
|
38
32
|
}
|
|
39
33
|
return queueName;
|
|
40
34
|
}
|
|
41
|
-
exports.
|
|
35
|
+
exports.ExchangeNameExtensions = {
|
|
42
36
|
"dead-letter": ".dlx",
|
|
43
37
|
retry: ".rtx",
|
|
44
38
|
default: undefined,
|
|
45
39
|
};
|
|
40
|
+
exports.QueueNameExtensions = {
|
|
41
|
+
"dead-letter": ".dlq",
|
|
42
|
+
retry: ".rtq",
|
|
43
|
+
default: undefined,
|
|
44
|
+
};
|
|
46
45
|
/**
|
|
47
46
|
* Returns the exchange name based on the configuration
|
|
48
47
|
* @param config
|
|
@@ -56,14 +55,14 @@ function getExchangeName(config) {
|
|
|
56
55
|
}
|
|
57
56
|
let exchangeName = config.name;
|
|
58
57
|
if (["dead-letter", "retry"].includes(queueType)) {
|
|
59
|
-
if (config.exchangeType === "topic") {
|
|
58
|
+
if (config.exchangeType === "topic" || config.exchangeType === "fanout") {
|
|
60
59
|
if (!config.topicSubscriberName) {
|
|
61
60
|
throw new errors_js_1.ConfigurationError(`Topic ${queueType} exchanges must have a subscriber name`);
|
|
62
61
|
}
|
|
63
62
|
exchangeName += "." + config.topicSubscriberName;
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
|
-
const ext = exports.
|
|
65
|
+
const ext = exports.ExchangeNameExtensions[queueType];
|
|
67
66
|
if (ext) {
|
|
68
67
|
exchangeName += ext;
|
|
69
68
|
}
|
|
@@ -74,9 +73,21 @@ function getQueueType(config) {
|
|
|
74
73
|
}
|
|
75
74
|
function getRoutingKey(config) {
|
|
76
75
|
var _a, _b;
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
const queueType = getQueueType(config);
|
|
77
|
+
const configuredRoutingKey = (_b = (_a = config.overrides) === null || _a === void 0 ? void 0 : _a.names[queueType]) === null || _b === void 0 ? void 0 : _b.routingKey;
|
|
78
|
+
if (configuredRoutingKey) {
|
|
79
|
+
return configuredRoutingKey;
|
|
80
|
+
}
|
|
81
|
+
const exchangeType = getExchangeType(config);
|
|
82
|
+
switch (exchangeType) {
|
|
83
|
+
case "direct":
|
|
84
|
+
return "default";
|
|
85
|
+
case "topic":
|
|
86
|
+
return "#";
|
|
87
|
+
case "fanout":
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
79
90
|
}
|
|
80
91
|
function getExchangeType(config) {
|
|
81
|
-
return config.exchangeType
|
|
92
|
+
return getQueueType(config) === "default" ? config.exchangeType : "fanout";
|
|
82
93
|
}
|
|
@@ -27,11 +27,15 @@ exports.RABBITMQ_TRANSPORT = "rabbitmq";
|
|
|
27
27
|
class RabbitMqConnection {
|
|
28
28
|
static create(config, logger) {
|
|
29
29
|
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
var _a;
|
|
30
31
|
const AmqpMgrPackage = yield (0, lazy_load_js_1.lazyLoad)({
|
|
31
32
|
packageName: "amqp-connection-manager",
|
|
32
33
|
context: "RabbitMqConnection",
|
|
33
34
|
});
|
|
34
|
-
return new RabbitMqConnection(config, AmqpMgrPackage.connect(config.host
|
|
35
|
+
return new RabbitMqConnection(config, AmqpMgrPackage.connect(config.host, (_a = config.options) !== null && _a !== void 0 ? _a : {
|
|
36
|
+
heartbeatIntervalInSeconds: 60,
|
|
37
|
+
reconnectTimeInSeconds: 5,
|
|
38
|
+
}), logger);
|
|
35
39
|
});
|
|
36
40
|
}
|
|
37
41
|
constructor(config, connection, logger) {
|
|
@@ -68,7 +72,7 @@ class RabbitMqConnection {
|
|
|
68
72
|
}
|
|
69
73
|
assertQueueAndBindings(channel, config) {
|
|
70
74
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
-
var _a, _b, _c
|
|
75
|
+
var _a, _b, _c;
|
|
72
76
|
let dlConfig, retryConfig;
|
|
73
77
|
({ config, dlConfig, retryConfig } = RabbitMqConnection.getConfigs(config));
|
|
74
78
|
const exchangeName = yield this.assertExchange(channel, config);
|
|
@@ -81,8 +85,8 @@ class RabbitMqConnection {
|
|
|
81
85
|
// special case to ensure the retry dead-letter only reverts to the original queue,
|
|
82
86
|
// regardless of original exchange type [that is, not re-using the original queue exchange here]
|
|
83
87
|
const retryDlExchangeName = ((_a = config.overrides) === null || _a === void 0 ? void 0 : _a.retryQueueExchangeName) ||
|
|
84
|
-
`${queueName}${config_js_1.QueueNameExtensions["
|
|
85
|
-
yield channel.assertExchange(retryDlExchangeName, "
|
|
88
|
+
`${queueName}${config_js_1.QueueNameExtensions["retry"]}${config_js_1.ExchangeNameExtensions["dead-letter"]}`;
|
|
89
|
+
yield channel.assertExchange(retryDlExchangeName, "fanout", {
|
|
86
90
|
autoDelete: !!config.temporary,
|
|
87
91
|
});
|
|
88
92
|
(_c = (_b = this.logger).debug) === null || _c === void 0 ? void 0 : _c.call(_b, `Asserted exchange ${retryDlExchangeName}`);
|
|
@@ -93,13 +97,13 @@ class RabbitMqConnection {
|
|
|
93
97
|
// create the default queue using the dead-letter exchange as the DLX
|
|
94
98
|
yield this.assertQueue(channel, queueName, autoDelete, dlExchangeName);
|
|
95
99
|
// bind the default queue using the default exchange and default routing key
|
|
96
|
-
yield this.bindQueue(channel, queueName, exchangeName, (
|
|
97
|
-
// bind the dead-letter queue to the dead-letter exchange
|
|
98
|
-
yield this.bindQueue(channel, dlQueueName, dlExchangeName,
|
|
99
|
-
// bind the retry queue to the retry exchange
|
|
100
|
-
yield this.bindQueue(channel, retryQueueName, retryExchangeName,
|
|
101
|
-
// bind the queue to the retry dead-letter exchange
|
|
102
|
-
yield this.bindQueue(channel, queueName, retryDlExchangeName,
|
|
100
|
+
yield this.bindQueue(channel, queueName, exchangeName, (0, config_js_1.getRoutingKey)(config));
|
|
101
|
+
// bind the dead-letter queue to the dead-letter exchange
|
|
102
|
+
yield this.bindQueue(channel, dlQueueName, dlExchangeName, "");
|
|
103
|
+
// bind the retry queue to the retry exchange
|
|
104
|
+
yield this.bindQueue(channel, retryQueueName, retryExchangeName, "");
|
|
105
|
+
// bind the queue to the retry dead-letter exchange
|
|
106
|
+
yield this.bindQueue(channel, queueName, retryDlExchangeName, "");
|
|
103
107
|
return {
|
|
104
108
|
exchangeName,
|
|
105
109
|
dlExchangeName,
|
package/package.json
CHANGED
package/src/rabbitmq/README.md
CHANGED
|
@@ -125,26 +125,37 @@ const config: RabbitQueueExchangeConfiguration = {
|
|
|
125
125
|
name: "my.queue",
|
|
126
126
|
transport: RABBITMQ_TRANSPORT,
|
|
127
127
|
schema: MyModelSchema,
|
|
128
|
+
exchangeType: "direct",
|
|
128
129
|
};
|
|
129
130
|
```
|
|
130
131
|
|
|
131
132
|
This configuration creates the following queues:
|
|
132
133
|
|
|
133
134
|
- `my.queue` the main queue which receives all messages published using the config
|
|
134
|
-
- dead-letter-exchange is set to `my.queue.dlx`
|
|
135
|
+
- `x-dead-letter-exchange` is set to `my.queue.dlx`
|
|
135
136
|
- `my.queue.dlq` the main queue's dead-letter queue where failed messages end up
|
|
137
|
+
- bound to `my.queue.dlx` exchange
|
|
136
138
|
- `my.queue.rtq` the main queue's retry queue where messages to be retried end up
|
|
137
|
-
-
|
|
139
|
+
- bound to `my.queue.rtx` exchange
|
|
140
|
+
- `x-dead-letter-exchange` is set to `my.queue.rtq.dlx`
|
|
141
|
+
- messages delivered here have an expiration
|
|
142
|
+
- when the message expires, it is removed from the queue and sent to the `my.queue.rtq.dlx` exchange which publishes the message back to the main queue `my.queue`
|
|
138
143
|
|
|
139
|
-
And these
|
|
144
|
+
And these exchanges:
|
|
140
145
|
|
|
141
146
|
- `my.queue` the main exchange that is bound to the main queue
|
|
142
|
-
- `my.queue.dlx`
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
147
|
+
- `my.queue.dlx` a fanout dead-letter exchange that receives failed messages from the main queue
|
|
148
|
+
- binding made to the `my.queue.dlq`
|
|
149
|
+
- configured as the `x-dead-letter-exchange` for the main queue `my.queue`
|
|
150
|
+
- `my.queue.rtx` a fanout retry exchange that receives messages to retry. it is bound to the retry queue
|
|
151
|
+
- binding made to the `my.queue.rtq`
|
|
152
|
+
- `my.queue.rtq.dlx` a fanout dead-letter exchange that receives retry messages from the retry queue
|
|
153
|
+
- binding made back to the original `my.queue`
|
|
154
|
+
- configured as the `x-dead-letter-exchange` for the retry queue `my.queue.rtq`
|
|
155
|
+
|
|
156
|
+
### Topic/Fanout exchanges
|
|
157
|
+
|
|
158
|
+
Topic/Fanout exchanges provide the ability to use a single exchange to copy messages to multiple queues simultaneously. When subscribing to a topic exchange, you must also provide a subscriber name to include in the queue name. This ensures that there is a unique queue for each subscriber of the exchange.
|
|
148
159
|
|
|
149
160
|
```ts
|
|
150
161
|
const config: RabbitQueueExchangeConfiguration = {
|