@zetra/citrineos-util 1.8.3-fork.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/dist/authorization/ApiAuthPlugin.d.ts +52 -0
- package/dist/authorization/ApiAuthPlugin.js +122 -0
- package/dist/authorization/ApiAuthPlugin.js.map +1 -0
- package/dist/authorization/OidcTokenProvider.d.ts +15 -0
- package/dist/authorization/OidcTokenProvider.js +47 -0
- package/dist/authorization/OidcTokenProvider.js.map +1 -0
- package/dist/authorization/index.d.ts +4 -0
- package/dist/authorization/index.js +8 -0
- package/dist/authorization/index.js.map +1 -0
- package/dist/authorization/provider/LocalByPassAuthProvider.d.ts +34 -0
- package/dist/authorization/provider/LocalByPassAuthProvider.js +62 -0
- package/dist/authorization/provider/LocalByPassAuthProvider.js.map +1 -0
- package/dist/authorization/provider/OIDCAuthProvider.d.ts +62 -0
- package/dist/authorization/provider/OIDCAuthProvider.js +173 -0
- package/dist/authorization/provider/OIDCAuthProvider.js.map +1 -0
- package/dist/authorization/rbac/RbacRulesLoader.d.ts +32 -0
- package/dist/authorization/rbac/RbacRulesLoader.js +105 -0
- package/dist/authorization/rbac/RbacRulesLoader.js.map +1 -0
- package/dist/authorization/rbac/UrlMatcher.d.ts +14 -0
- package/dist/authorization/rbac/UrlMatcher.js +44 -0
- package/dist/authorization/rbac/UrlMatcher.js.map +1 -0
- package/dist/authorizer/RealTimeAuthorizer.d.ts +28 -0
- package/dist/authorizer/RealTimeAuthorizer.js +152 -0
- package/dist/authorizer/RealTimeAuthorizer.js.map +1 -0
- package/dist/authorizer/index.d.ts +1 -0
- package/dist/authorizer/index.js +5 -0
- package/dist/authorizer/index.js.map +1 -0
- package/dist/cache/memory.d.ts +19 -0
- package/dist/cache/memory.js +147 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/redis.d.ts +16 -0
- package/dist/cache/redis.js +120 -0
- package/dist/cache/redis.js.map +1 -0
- package/dist/certificate/CertificateAuthority.d.ts +38 -0
- package/dist/certificate/CertificateAuthority.js +233 -0
- package/dist/certificate/CertificateAuthority.js.map +1 -0
- package/dist/certificate/CertificateUtil.d.ts +60 -0
- package/dist/certificate/CertificateUtil.js +317 -0
- package/dist/certificate/CertificateUtil.js.map +1 -0
- package/dist/certificate/client/acme.d.ts +37 -0
- package/dist/certificate/client/acme.js +138 -0
- package/dist/certificate/client/acme.js.map +1 -0
- package/dist/certificate/client/hubject.d.ts +41 -0
- package/dist/certificate/client/hubject.js +221 -0
- package/dist/certificate/client/hubject.js.map +1 -0
- package/dist/certificate/client/interface.d.ts +12 -0
- package/dist/certificate/client/interface.js +5 -0
- package/dist/certificate/client/interface.js.map +1 -0
- package/dist/certificate/index.d.ts +2 -0
- package/dist/certificate/index.js +6 -0
- package/dist/certificate/index.js.map +1 -0
- package/dist/files/ftpServer.d.ts +4 -0
- package/dist/files/ftpServer.js +9 -0
- package/dist/files/ftpServer.js.map +1 -0
- package/dist/files/gcpCloudStorage.d.ts +39 -0
- package/dist/files/gcpCloudStorage.js +130 -0
- package/dist/files/gcpCloudStorage.js.map +1 -0
- package/dist/files/localStorage.d.ts +14 -0
- package/dist/files/localStorage.js +57 -0
- package/dist/files/localStorage.js.map +1 -0
- package/dist/files/s3Storage.d.ts +17 -0
- package/dist/files/s3Storage.js +118 -0
- package/dist/files/s3Storage.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/networkconnection/WebsocketNetworkConnection.d.ts +135 -0
- package/dist/networkconnection/WebsocketNetworkConnection.js +474 -0
- package/dist/networkconnection/WebsocketNetworkConnection.js.map +1 -0
- package/dist/networkconnection/authenticator/Authenticator.d.ts +20 -0
- package/dist/networkconnection/authenticator/Authenticator.js +39 -0
- package/dist/networkconnection/authenticator/Authenticator.js.map +1 -0
- package/dist/networkconnection/authenticator/AuthenticatorFilter.d.ts +11 -0
- package/dist/networkconnection/authenticator/AuthenticatorFilter.js +30 -0
- package/dist/networkconnection/authenticator/AuthenticatorFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/BasicAuthenticationFilter.d.ts +17 -0
- package/dist/networkconnection/authenticator/BasicAuthenticationFilter.js +51 -0
- package/dist/networkconnection/authenticator/BasicAuthenticationFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/ConnectedStationFilter.d.ts +14 -0
- package/dist/networkconnection/authenticator/ConnectedStationFilter.js +25 -0
- package/dist/networkconnection/authenticator/ConnectedStationFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/NetworkProfileFilter.d.ts +16 -0
- package/dist/networkconnection/authenticator/NetworkProfileFilter.js +84 -0
- package/dist/networkconnection/authenticator/NetworkProfileFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/UnknownStationFilter.d.ts +16 -0
- package/dist/networkconnection/authenticator/UnknownStationFilter.js +25 -0
- package/dist/networkconnection/authenticator/UnknownStationFilter.js.map +1 -0
- package/dist/networkconnection/authenticator/errors/AuthenticationError.d.ts +6 -0
- package/dist/networkconnection/authenticator/errors/AuthenticationError.js +25 -0
- package/dist/networkconnection/authenticator/errors/AuthenticationError.js.map +1 -0
- package/dist/networkconnection/authenticator/errors/IUpgradeError.d.ts +9 -0
- package/dist/networkconnection/authenticator/errors/IUpgradeError.js +5 -0
- package/dist/networkconnection/authenticator/errors/IUpgradeError.js.map +1 -0
- package/dist/networkconnection/authenticator/errors/UnknownError.d.ts +6 -0
- package/dist/networkconnection/authenticator/errors/UnknownError.js +24 -0
- package/dist/networkconnection/authenticator/errors/UnknownError.js.map +1 -0
- package/dist/networkconnection/index.d.ts +5 -0
- package/dist/networkconnection/index.js +9 -0
- package/dist/networkconnection/index.js.map +1 -0
- package/dist/queue/index.d.ts +4 -0
- package/dist/queue/index.js +8 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/kafka/receiver.d.ts +35 -0
- package/dist/queue/kafka/receiver.js +179 -0
- package/dist/queue/kafka/receiver.js.map +1 -0
- package/dist/queue/kafka/sender.d.ts +53 -0
- package/dist/queue/kafka/sender.js +189 -0
- package/dist/queue/kafka/sender.js.map +1 -0
- package/dist/queue/rabbit-mq/receiver.d.ts +89 -0
- package/dist/queue/rabbit-mq/receiver.js +472 -0
- package/dist/queue/rabbit-mq/receiver.js.map +1 -0
- package/dist/queue/rabbit-mq/sender.d.ts +90 -0
- package/dist/queue/rabbit-mq/sender.js +251 -0
- package/dist/queue/rabbit-mq/sender.js.map +1 -0
- package/dist/security/SignedMeterValuesUtil.d.ts +44 -0
- package/dist/security/SignedMeterValuesUtil.js +135 -0
- package/dist/security/SignedMeterValuesUtil.js.map +1 -0
- package/dist/security/authentication.d.ts +2 -0
- package/dist/security/authentication.js +26 -0
- package/dist/security/authentication.js.map +1 -0
- package/dist/util/RequestOperations.d.ts +14 -0
- package/dist/util/RequestOperations.js +25 -0
- package/dist/util/RequestOperations.js.map +1 -0
- package/dist/util/StringOperations.d.ts +1 -0
- package/dist/util/StringOperations.js +8 -0
- package/dist/util/StringOperations.js.map +1 -0
- package/dist/util/emaidCheckDigitCalculator.d.ts +15 -0
- package/dist/util/emaidCheckDigitCalculator.js +179 -0
- package/dist/util/emaidCheckDigitCalculator.js.map +1 -0
- package/dist/util/idGenerator.d.ts +7 -0
- package/dist/util/idGenerator.js +10 -0
- package/dist/util/idGenerator.js.map +1 -0
- package/dist/util/parser.d.ts +31 -0
- package/dist/util/parser.js +60 -0
- package/dist/util/parser.js.map +1 -0
- package/dist/util/swagger.d.ts +5 -0
- package/dist/util/swagger.js +154 -0
- package/dist/util/swagger.js.map +1 -0
- package/dist/util/validator.d.ts +110 -0
- package/dist/util/validator.js +534 -0
- package/dist/util/validator.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2025 Contributors to the CitrineOS Project
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
import { AbstractMessageHandler, CacheNamespace, CircuitBreaker, Message, RetryMessageError, } from '@citrineos/base';
|
|
5
|
+
import * as amqplib from 'amqplib';
|
|
6
|
+
import { Logger } from 'tslog';
|
|
7
|
+
import { MemoryCache } from '../../index.js';
|
|
8
|
+
/**
|
|
9
|
+
* Implementation of a {@link IMessageHandler} using RabbitMQ as the underlying transport.
|
|
10
|
+
*/
|
|
11
|
+
export class RabbitMqReceiver extends AbstractMessageHandler {
|
|
12
|
+
/**
|
|
13
|
+
* Constants
|
|
14
|
+
*/
|
|
15
|
+
static QUEUE_PREFIX = 'rabbit_queue_';
|
|
16
|
+
static CACHE_PREFIX = 'rabbit_subscription_';
|
|
17
|
+
static METADATA_PREFIX = 'rabbit_subscription_metadata_';
|
|
18
|
+
static REGISTRY_KEY = 'rabbit_subscription_registry';
|
|
19
|
+
static RECONNECT_DELAY = 5000;
|
|
20
|
+
/**
|
|
21
|
+
* Fields
|
|
22
|
+
*/
|
|
23
|
+
_cache;
|
|
24
|
+
_connection;
|
|
25
|
+
_channel;
|
|
26
|
+
_abortReconnectController;
|
|
27
|
+
_circuitBreaker;
|
|
28
|
+
_reconnectInterval;
|
|
29
|
+
constructor(config, logger, module, cache, circuitBreaker) {
|
|
30
|
+
super(config, logger, module);
|
|
31
|
+
this._cache = cache || new MemoryCache();
|
|
32
|
+
this._circuitBreaker = circuitBreaker ?? new CircuitBreaker();
|
|
33
|
+
this._circuitBreaker.onStateChange(this._onCircuitBreakerStateChange.bind(this));
|
|
34
|
+
}
|
|
35
|
+
async initConnection() {
|
|
36
|
+
if (this._circuitBreaker.state === 'CLOSED') {
|
|
37
|
+
throw new Error('Circuit breaker is CLOSED. Cannot initialize RabbitMQ connection.');
|
|
38
|
+
}
|
|
39
|
+
this._abortReconnectController = new AbortController();
|
|
40
|
+
this._channel = await this._connectWithRetry(this._abortReconnectController.signal);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Methods
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* Binds queue to an exchange given identifier and optional actions and filter.
|
|
47
|
+
* Note: Due to the nature of AMQP 0-9-1 model, if you need to filter for the identifier, you **MUST** provide it in the filter object.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} identifier - The identifier of the channel to subscribe to.
|
|
50
|
+
* @param {CallAction[]} actions - Optional. An array of actions to filter the messages.
|
|
51
|
+
* @param {{ [k: string]: string; }} filter - Optional. An object representing the filter to apply on the messages.
|
|
52
|
+
* @return {Promise<boolean>} A promise that resolves to true if the subscription is successful, false otherwise.
|
|
53
|
+
*/
|
|
54
|
+
async subscribe(identifier, actions, filter) {
|
|
55
|
+
// If actions are a defined but empty list, it is likely a module
|
|
56
|
+
// with no available actions and should not have a queue.
|
|
57
|
+
//
|
|
58
|
+
// If actions are undefined, it is likely a charger,
|
|
59
|
+
// which is "allowed" not to have actions.
|
|
60
|
+
if (actions && actions.length === 0) {
|
|
61
|
+
this._logger.debug(`Skipping queue binding for module ${identifier} as there are no available actions.`);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
const exchange = this._config.util.messageBroker.amqp?.exchange;
|
|
65
|
+
const queueName = `${RabbitMqReceiver.QUEUE_PREFIX}${identifier}`;
|
|
66
|
+
// Ensure that filter includes the x-match header set to all
|
|
67
|
+
filter = filter
|
|
68
|
+
? {
|
|
69
|
+
'x-match': 'all',
|
|
70
|
+
...filter,
|
|
71
|
+
}
|
|
72
|
+
: { 'x-match': 'all' };
|
|
73
|
+
if (!this._channel) {
|
|
74
|
+
throw new Error('RabbitMQ is down: cannot subscribe.');
|
|
75
|
+
}
|
|
76
|
+
const channel = this._channel;
|
|
77
|
+
// Assert exchange and queue
|
|
78
|
+
await channel.assertExchange(exchange, 'headers', { durable: false });
|
|
79
|
+
await channel.assertQueue(queueName, {
|
|
80
|
+
durable: false,
|
|
81
|
+
autoDelete: true,
|
|
82
|
+
exclusive: false,
|
|
83
|
+
});
|
|
84
|
+
// Bind queue based on provided actions and filters
|
|
85
|
+
if (actions && actions.length > 0) {
|
|
86
|
+
for (const action of actions) {
|
|
87
|
+
this._logger.debug(`Bind ${queueName} on ${exchange} for ${action} with filter ${JSON.stringify(filter)}.`);
|
|
88
|
+
await channel.bindQueue(queueName, exchange, '', { action, ...filter });
|
|
89
|
+
this._logger.info(`Queue ${queueName} bound to exchange ${exchange} for action ${action} with filter ${JSON.stringify(filter)}.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this._logger.debug(`Bind ${queueName} on ${exchange} with filter ${JSON.stringify(filter)}.`);
|
|
94
|
+
await channel.bindQueue(queueName, exchange, '', filter);
|
|
95
|
+
this._logger.info(`Queue ${queueName} bound to exchange ${exchange} with filter ${JSON.stringify(filter)}.`);
|
|
96
|
+
}
|
|
97
|
+
// Start consuming messages
|
|
98
|
+
await channel.consume(queueName, (msg) => this._onMessage(msg, channel));
|
|
99
|
+
// Define cache keys
|
|
100
|
+
const cacheKey = `${RabbitMqReceiver.CACHE_PREFIX}${identifier}`;
|
|
101
|
+
const metadataKey = `${RabbitMqReceiver.METADATA_PREFIX}${identifier}`;
|
|
102
|
+
// Retrieve cached queue names
|
|
103
|
+
const cachedQueues = await this._cache
|
|
104
|
+
.get(cacheKey, CacheNamespace.Other, () => Array)
|
|
105
|
+
.then((value) => {
|
|
106
|
+
if (value) {
|
|
107
|
+
value.push(queueName);
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
return new Array(queueName);
|
|
111
|
+
});
|
|
112
|
+
// Add queue name to cache
|
|
113
|
+
await this._cache.set(cacheKey, JSON.stringify(cachedQueues), CacheNamespace.Other);
|
|
114
|
+
// Store subscription metadata for re-subscription after reconnect
|
|
115
|
+
const subscriptionMetadata = {
|
|
116
|
+
identifier,
|
|
117
|
+
actions,
|
|
118
|
+
filter,
|
|
119
|
+
};
|
|
120
|
+
await this._cache.set(metadataKey, JSON.stringify(subscriptionMetadata), CacheNamespace.Other);
|
|
121
|
+
// Add identifier to registry (list of all active subscriptions)
|
|
122
|
+
const registry = await this._cache
|
|
123
|
+
.get(RabbitMqReceiver.REGISTRY_KEY, CacheNamespace.Other, () => Array)
|
|
124
|
+
.then((value) => {
|
|
125
|
+
if (value && !value.includes(identifier)) {
|
|
126
|
+
value.push(identifier);
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
else if (!value) {
|
|
130
|
+
return new Array(identifier);
|
|
131
|
+
}
|
|
132
|
+
return value; // Already in registry
|
|
133
|
+
});
|
|
134
|
+
await this._cache.set(RabbitMqReceiver.REGISTRY_KEY, JSON.stringify(registry), CacheNamespace.Other);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
unsubscribe(identifier) {
|
|
138
|
+
const cacheKey = `${RabbitMqReceiver.CACHE_PREFIX}${identifier}`;
|
|
139
|
+
const metadataKey = `${RabbitMqReceiver.METADATA_PREFIX}${identifier}`;
|
|
140
|
+
return this._cache
|
|
141
|
+
.get(cacheKey, CacheNamespace.Other, () => Array)
|
|
142
|
+
.then(async (queues) => {
|
|
143
|
+
if (queues) {
|
|
144
|
+
if (!this._channel) {
|
|
145
|
+
throw new Error('RabbitMQ is down: cannot unsubscribe.');
|
|
146
|
+
}
|
|
147
|
+
const channel = this._channel;
|
|
148
|
+
this._channel = channel;
|
|
149
|
+
for (const queue of queues) {
|
|
150
|
+
await channel.unbindQueue(queue, this._config.util.messageBroker.amqp?.exchange || '', '');
|
|
151
|
+
const messageCount = await this._channel?.deleteQueue(queue);
|
|
152
|
+
this._logger.info(`Queue ${identifier} deleted with ${messageCount?.messageCount} messages remaining.`);
|
|
153
|
+
}
|
|
154
|
+
// Remove the cache entries after successfully deleting all queues
|
|
155
|
+
await this._cache.remove(cacheKey, CacheNamespace.Other);
|
|
156
|
+
await this._cache.remove(metadataKey, CacheNamespace.Other);
|
|
157
|
+
// Remove identifier from registry
|
|
158
|
+
const registry = await this._cache.get(RabbitMqReceiver.REGISTRY_KEY, CacheNamespace.Other, () => Array);
|
|
159
|
+
if (registry) {
|
|
160
|
+
const updatedRegistry = registry.filter((id) => id !== identifier);
|
|
161
|
+
if (updatedRegistry.length === 0) {
|
|
162
|
+
await this._cache.remove(RabbitMqReceiver.REGISTRY_KEY, CacheNamespace.Other);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
await this._cache.set(RabbitMqReceiver.REGISTRY_KEY, JSON.stringify(updatedRegistry), CacheNamespace.Other);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this._logger.warn(`Failed to delete queue for ${identifier}, queue name not found in cache.`);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
shutdown() {
|
|
177
|
+
this._abortReconnectController?.abort();
|
|
178
|
+
return Promise.resolve();
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Protected Methods
|
|
182
|
+
*/
|
|
183
|
+
/**
|
|
184
|
+
* Connect to RabbitMQ with retry logic.
|
|
185
|
+
* This method will keep trying to connect until successful, unless aborted.
|
|
186
|
+
*
|
|
187
|
+
* @param {AbortSignal} [abortSignal] - Optional abort signal to stop retrying.
|
|
188
|
+
* @return {Promise<amqplib.Channel>} A promise that resolves to the AMQP channel.
|
|
189
|
+
*/
|
|
190
|
+
async _connectWithRetry(abortSignal) {
|
|
191
|
+
const url = this._config.util.messageBroker.amqp?.url;
|
|
192
|
+
if (!url) {
|
|
193
|
+
throw new Error('RabbitMQ URL is not configured');
|
|
194
|
+
}
|
|
195
|
+
while (true) {
|
|
196
|
+
if (abortSignal?.aborted) {
|
|
197
|
+
this._logger.warn('RabbitMQ reconnect aborted by signal.');
|
|
198
|
+
throw new Error('RabbitMQ reconnect aborted');
|
|
199
|
+
}
|
|
200
|
+
if (this._circuitBreaker.state === 'CLOSED') {
|
|
201
|
+
throw new Error('Circuit breaker is CLOSED. Cannot connect to RabbitMQ.');
|
|
202
|
+
}
|
|
203
|
+
try {
|
|
204
|
+
const connection = await amqplib.connect(url, { heartbeat: 10 });
|
|
205
|
+
this._connection = connection;
|
|
206
|
+
const channel = await connection.createChannel();
|
|
207
|
+
const exchange = this._config.util.messageBroker.amqp?.exchange;
|
|
208
|
+
if (exchange) {
|
|
209
|
+
await channel.assertExchange(exchange, 'headers', { durable: false });
|
|
210
|
+
}
|
|
211
|
+
channel.on('error', (err) => {
|
|
212
|
+
this._logger.error('AMQP channel error', err);
|
|
213
|
+
});
|
|
214
|
+
this._setupConnectionListeners();
|
|
215
|
+
this._circuitBreaker.triggerSuccess();
|
|
216
|
+
// Re-subscribe to all cached subscriptions after successful reconnection
|
|
217
|
+
await this._resubscribeAll();
|
|
218
|
+
return channel;
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
this._logger.error('RabbitMQ connect failed, triggering circuit breaker failure', err);
|
|
222
|
+
this._circuitBreaker.triggerFailure(err?.message);
|
|
223
|
+
// Wait for circuit breaker to allow retry (exponential backoff handled by circuit breaker)
|
|
224
|
+
await new Promise((res) => setTimeout(res, 1000));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
_startReconnectInterval() {
|
|
229
|
+
if (this._reconnectInterval) {
|
|
230
|
+
clearInterval(this._reconnectInterval);
|
|
231
|
+
}
|
|
232
|
+
const delay = (this._config.maxReconnectDelay || 30) * 1000;
|
|
233
|
+
this._logger.warn(`Starting continuous reconnect attempts every ${delay / 1000} seconds while circuit breaker is CLOSED.`);
|
|
234
|
+
this._reconnectInterval = setInterval(() => {
|
|
235
|
+
this._logger.info('Attempting RabbitMQ reconnect due to circuit breaker CLOSED...');
|
|
236
|
+
this._connectWithRetry()
|
|
237
|
+
.then((channel) => {
|
|
238
|
+
this._logger.info('RabbitMQ reconnect attempt succeeded.');
|
|
239
|
+
this._channel = channel;
|
|
240
|
+
this._circuitBreaker.triggerSuccess();
|
|
241
|
+
})
|
|
242
|
+
.catch((err) => {
|
|
243
|
+
this._logger.error('RabbitMQ reconnect attempt failed.', err);
|
|
244
|
+
});
|
|
245
|
+
}, delay);
|
|
246
|
+
}
|
|
247
|
+
_onCircuitBreakerStateChange(state, reason) {
|
|
248
|
+
this._logger.info(`[CircuitBreaker] State changed to ${state}${reason ? `: ${reason}` : ''}`);
|
|
249
|
+
switch (state) {
|
|
250
|
+
case 'CLOSED': {
|
|
251
|
+
this._logger.error('Circuit breaker CLOSED: shutting down RabbitMQ receiver. Reason:', reason);
|
|
252
|
+
void this.shutdown();
|
|
253
|
+
if (this._reconnectInterval) {
|
|
254
|
+
this._logger.info('Clearing reconnect interval as circuit breaker is now CLOSED.');
|
|
255
|
+
clearInterval(this._reconnectInterval);
|
|
256
|
+
this._reconnectInterval = undefined;
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
case 'OPEN': {
|
|
261
|
+
this._logger.info('Circuit breaker is OPEN. Will attempt to (re)initialize RabbitMQ connection.');
|
|
262
|
+
if (this._reconnectInterval) {
|
|
263
|
+
this._logger.info('Clearing reconnect interval as circuit breaker is now OPEN.');
|
|
264
|
+
clearInterval(this._reconnectInterval);
|
|
265
|
+
this._reconnectInterval = undefined;
|
|
266
|
+
}
|
|
267
|
+
this._connectWithRetry()
|
|
268
|
+
.then((channel) => {
|
|
269
|
+
this._logger.info('RabbitMQ connection (re)initialized.');
|
|
270
|
+
this._channel = channel;
|
|
271
|
+
this._circuitBreaker.triggerSuccess();
|
|
272
|
+
})
|
|
273
|
+
.catch((err) => {
|
|
274
|
+
this._logger.error('RabbitMQ (re)init failed.', err);
|
|
275
|
+
});
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
case 'FAILING': {
|
|
279
|
+
this._logger.warn('Circuit breaker is FAILING. RabbitMQ receiver will not receive messages until recovery. Reason:', reason);
|
|
280
|
+
this._logger.info('Attempting to start reconnect interval after circuit breaker FAILING.');
|
|
281
|
+
this._startReconnectInterval();
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
default:
|
|
285
|
+
this._logger.warn('Unknown circuit breaker state:', state);
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Setup listeners for connection and channel events.
|
|
291
|
+
* This will handle disconnections and errors.
|
|
292
|
+
* Ensures listeners are not attached multiple times to the same connection.
|
|
293
|
+
*/
|
|
294
|
+
_setupConnectionListeners() {
|
|
295
|
+
if (this._connection) {
|
|
296
|
+
// Only attach listeners if not already attached to this connection
|
|
297
|
+
if (this._connection._listenersAttached)
|
|
298
|
+
return;
|
|
299
|
+
this._connection.removeAllListeners('close');
|
|
300
|
+
this._connection.removeAllListeners('error');
|
|
301
|
+
this._connection.on('close', () => this._handleDisconnect());
|
|
302
|
+
this._connection.on('error', () => this._handleDisconnect());
|
|
303
|
+
this._connection._listenersAttached = true;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Handle RabbitMQ disconnection.
|
|
308
|
+
* This method will attempt to reconnect to RabbitMQ when the connection is lost.
|
|
309
|
+
* Debounces concurrent reconnects.
|
|
310
|
+
*/
|
|
311
|
+
async _handleDisconnect() {
|
|
312
|
+
this._logger.warn('RabbitMQ connection lost. Triggering circuit breaker failure.');
|
|
313
|
+
this._connection = undefined;
|
|
314
|
+
this._channel = undefined;
|
|
315
|
+
this._circuitBreaker.triggerFailure('RabbitMQ connection lost');
|
|
316
|
+
if (this._circuitBreaker.state === 'CLOSED') {
|
|
317
|
+
this._startReconnectInterval();
|
|
318
|
+
}
|
|
319
|
+
if (this._circuitBreaker.state === 'FAILING') {
|
|
320
|
+
this._logger.warn('Circuit breaker is FAILING. RabbitMQ receiver will not receive messages until recovery.');
|
|
321
|
+
this._startReconnectInterval();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Re-subscribe to all cached subscriptions after reconnection.
|
|
326
|
+
* This ensures queues are recreated with the same bindings.
|
|
327
|
+
* Uses cache to retrieve subscription metadata.
|
|
328
|
+
*/
|
|
329
|
+
async _resubscribeAll() {
|
|
330
|
+
if (!this._channel) {
|
|
331
|
+
this._logger.warn('Cannot re-subscribe: channel not available.');
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
// Get registry of all active subscription identifiers
|
|
336
|
+
const registry = await this._cache.get(RabbitMqReceiver.REGISTRY_KEY, CacheNamespace.Other, () => Array);
|
|
337
|
+
if (!registry || registry.length === 0) {
|
|
338
|
+
this._logger.debug('No active subscriptions found in cache to re-subscribe.');
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
this._logger.info(`Re-subscribing ${registry.length} active subscription(s) after RabbitMQ reconnection.`);
|
|
342
|
+
// Re-subscribe to all subscriptions in registry
|
|
343
|
+
const resubscribePromises = [];
|
|
344
|
+
for (const identifier of registry) {
|
|
345
|
+
// Get subscription metadata from cache
|
|
346
|
+
const metadataKey = `${RabbitMqReceiver.METADATA_PREFIX}${identifier}`;
|
|
347
|
+
const metadataJson = await this._cache.get(metadataKey, CacheNamespace.Other);
|
|
348
|
+
if (!metadataJson) {
|
|
349
|
+
this._logger.warn(`Subscription metadata not found for ${identifier}, skipping re-subscription.`);
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
try {
|
|
353
|
+
// Parse the JSON metadata to ensure proper deserialization
|
|
354
|
+
const metadata = JSON.parse(metadataJson);
|
|
355
|
+
// Validate metadata structure
|
|
356
|
+
if (!metadata.identifier || metadata.identifier !== identifier) {
|
|
357
|
+
this._logger.warn(`Subscription metadata identifier mismatch for ${identifier}, skipping re-subscription.`);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
this._logger.debug(`Re-subscribing identifier: ${identifier} with actions: ${JSON.stringify(metadata.actions)} and filter: ${JSON.stringify(metadata.filter)}`);
|
|
361
|
+
resubscribePromises.push(this._resubscribe(identifier, metadata.actions, metadata.filter).catch((error) => {
|
|
362
|
+
this._logger.error(`Failed to re-subscribe ${identifier}:`, error);
|
|
363
|
+
return false;
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
366
|
+
catch (parseError) {
|
|
367
|
+
this._logger.error(`Failed to parse subscription metadata for ${identifier}:`, parseError, metadataJson);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const results = await Promise.all(resubscribePromises);
|
|
371
|
+
const successCount = results.filter((r) => r === true).length;
|
|
372
|
+
this._logger.info(`Re-subscription complete: ${successCount}/${registry.length} subscriptions recreated successfully.`);
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
this._logger.error('Error during re-subscription:', error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Internal method to re-subscribe without the channel check.
|
|
380
|
+
* Used during reconnection to recreate queues.
|
|
381
|
+
*/
|
|
382
|
+
async _resubscribe(identifier, actions, filter) {
|
|
383
|
+
// If actions are a defined but empty list, skip (modules with no actions shouldn't have queues)
|
|
384
|
+
if (actions && actions.length === 0) {
|
|
385
|
+
this._logger.debug(`Skipping re-subscription for ${identifier}: actions array is empty.`);
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
// Log warning if actions is undefined for module subscriptions (should have actions)
|
|
389
|
+
// Router subscriptions (like "1_cp001") can have undefined actions, but module subscriptions should have them
|
|
390
|
+
if ((!actions && identifier.includes('_requests')) || identifier.includes('_responses')) {
|
|
391
|
+
this._logger.warn(`Re-subscribing module subscription ${identifier} without actions filter - this may cause messages to be routed incorrectly!`);
|
|
392
|
+
}
|
|
393
|
+
const exchange = this._config.util.messageBroker.amqp?.exchange;
|
|
394
|
+
const queueName = `${RabbitMqReceiver.QUEUE_PREFIX}${identifier}_${Date.now()}`;
|
|
395
|
+
// Ensure that filter includes the x-match header set to all
|
|
396
|
+
filter = filter
|
|
397
|
+
? {
|
|
398
|
+
'x-match': 'all',
|
|
399
|
+
...filter,
|
|
400
|
+
}
|
|
401
|
+
: { 'x-match': 'all' };
|
|
402
|
+
if (!this._channel) {
|
|
403
|
+
throw new Error('RabbitMQ is down: cannot re-subscribe.');
|
|
404
|
+
}
|
|
405
|
+
const channel = this._channel;
|
|
406
|
+
// Assert exchange and queue
|
|
407
|
+
await channel.assertExchange(exchange, 'headers', { durable: false });
|
|
408
|
+
await channel.assertQueue(queueName, {
|
|
409
|
+
durable: false,
|
|
410
|
+
autoDelete: true,
|
|
411
|
+
exclusive: false,
|
|
412
|
+
});
|
|
413
|
+
// Bind queue based on provided actions and filters
|
|
414
|
+
if (actions && actions.length > 0) {
|
|
415
|
+
for (const action of actions) {
|
|
416
|
+
this._logger.debug(`Re-bind ${queueName} on ${exchange} for ${action} with filter ${JSON.stringify(filter)}.`);
|
|
417
|
+
await channel.bindQueue(queueName, exchange, '', { action, ...filter });
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
this._logger.debug(`Re-bind ${queueName} on ${exchange} with filter ${JSON.stringify(filter)}.`);
|
|
422
|
+
await channel.bindQueue(queueName, exchange, '', filter);
|
|
423
|
+
}
|
|
424
|
+
// Start consuming messages
|
|
425
|
+
await channel.consume(queueName, (msg) => this._onMessage(msg, channel));
|
|
426
|
+
// Update cache with new queue name
|
|
427
|
+
const cacheKey = `${RabbitMqReceiver.CACHE_PREFIX}${identifier}`;
|
|
428
|
+
const cachedQueues = await this._cache
|
|
429
|
+
.get(cacheKey, CacheNamespace.Other, () => Array)
|
|
430
|
+
.then((value) => {
|
|
431
|
+
if (value) {
|
|
432
|
+
value.push(queueName);
|
|
433
|
+
return value;
|
|
434
|
+
}
|
|
435
|
+
return new Array(queueName);
|
|
436
|
+
});
|
|
437
|
+
await this._cache.set(cacheKey, JSON.stringify(cachedQueues), CacheNamespace.Other);
|
|
438
|
+
this._logger.debug(`Successfully re-subscribed ${identifier} with queue ${queueName}`);
|
|
439
|
+
return true;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Underlying RabbitMQ message handler.
|
|
443
|
+
*
|
|
444
|
+
* @param message The AMQPMessage to process
|
|
445
|
+
* @param channel
|
|
446
|
+
*/
|
|
447
|
+
async _onMessage(message, channel) {
|
|
448
|
+
if (message) {
|
|
449
|
+
try {
|
|
450
|
+
this._logger.debug('_onMessage:Received message:', message.properties, message.content.toString());
|
|
451
|
+
const messageData = JSON.parse(message.content.toString());
|
|
452
|
+
// Create Message instance with generic payload (no type transformation needed)
|
|
453
|
+
const parsed = new Message(messageData.origin || messageData._origin, messageData.eventGroup || messageData._eventGroup, messageData.action || messageData._action, messageData.state || messageData._state, messageData.context || messageData._context, messageData.payload || messageData._payload, // Keep payload as generic object
|
|
454
|
+
messageData.protocol || messageData._protocol);
|
|
455
|
+
await this.handle(parsed, message.properties);
|
|
456
|
+
}
|
|
457
|
+
catch (error) {
|
|
458
|
+
if (error instanceof RetryMessageError) {
|
|
459
|
+
this._logger.warn('Retrying message: ', error.message);
|
|
460
|
+
// Retryable error, usually ongoing call with station when trying to send new call
|
|
461
|
+
channel.nack(message);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
this._logger.error('Error while processing message:', error, message);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
channel.ack(message);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
//# sourceMappingURL=receiver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receiver.js","sourceRoot":"","sources":["../../../src/queue/rabbit-mq/receiver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { IMessage, IMessageConfirmation, IMessageSender, OcppRequest, OcppResponse, SystemConfig } from '@citrineos/base';
|
|
2
|
+
import { AbstractMessageSender, CircuitBreaker, MessageState, OcppError } from '@citrineos/base';
|
|
3
|
+
import * as amqplib from 'amqplib';
|
|
4
|
+
import type { ILogObj } from 'tslog';
|
|
5
|
+
import { Logger } from 'tslog';
|
|
6
|
+
/**
|
|
7
|
+
* Implementation of a {@link IMessageSender} using RabbitMQ as the underlying transport.
|
|
8
|
+
*/
|
|
9
|
+
export declare class RabbitMqSender extends AbstractMessageSender implements IMessageSender {
|
|
10
|
+
/**
|
|
11
|
+
* Constants
|
|
12
|
+
*/
|
|
13
|
+
private static readonly QUEUE_PREFIX;
|
|
14
|
+
private static readonly RECONNECT_DELAY;
|
|
15
|
+
/**
|
|
16
|
+
* Fields
|
|
17
|
+
*/
|
|
18
|
+
protected _connection?: amqplib.Connection;
|
|
19
|
+
protected _channel?: amqplib.Channel;
|
|
20
|
+
private _reconnecting;
|
|
21
|
+
private _abortReconnectController?;
|
|
22
|
+
private _circuitBreaker;
|
|
23
|
+
private _reconnectInterval?;
|
|
24
|
+
/**
|
|
25
|
+
* Constructor for the class.
|
|
26
|
+
*
|
|
27
|
+
* @param {SystemConfig} config - The system configuration.
|
|
28
|
+
* @param {Logger<ILogObj>} [logger] - The logger object.
|
|
29
|
+
*/
|
|
30
|
+
constructor(config: SystemConfig, logger?: Logger<ILogObj>, circuitBreaker?: CircuitBreaker);
|
|
31
|
+
/**
|
|
32
|
+
* Methods
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Sends a request message with an optional payload and returns a promise that resolves to the confirmation message.
|
|
36
|
+
*
|
|
37
|
+
* @param {IMessage<OcppRequest>} message - The message to be sent.
|
|
38
|
+
* @param {OcppRequest | undefined} payload - The optional payload to be sent with the message.
|
|
39
|
+
* @return {Promise<IMessageConfirmation>} A promise that resolves to the confirmation message.
|
|
40
|
+
*/
|
|
41
|
+
sendRequest(message: IMessage<OcppRequest>, payload?: OcppRequest | undefined): Promise<IMessageConfirmation>;
|
|
42
|
+
/**
|
|
43
|
+
* Sends a response message and returns a promise of the message confirmation.
|
|
44
|
+
*
|
|
45
|
+
* @param {IMessage<OcppResponse | OcppError>} message - The message to send.
|
|
46
|
+
* @param {OcppResponse | OcppError} payload - The payload to include in the response.
|
|
47
|
+
* @return {Promise<IMessageConfirmation>} - A promise that resolves to the message confirmation.
|
|
48
|
+
*/
|
|
49
|
+
sendResponse(message: IMessage<OcppResponse | OcppError>, payload?: OcppResponse | OcppError): Promise<IMessageConfirmation>;
|
|
50
|
+
/**
|
|
51
|
+
* Sends a message and returns a promise that resolves to a message confirmation.
|
|
52
|
+
*
|
|
53
|
+
* @param {IMessage<OcppRequest | OcppResponse | OcppError>} message - The message to be sent.
|
|
54
|
+
* @param {OcppRequest | OcppResponse | OcppError} [payload] - The payload to be included in the message.
|
|
55
|
+
* @param {MessageState} [state] - The state of the message.
|
|
56
|
+
* @return {Promise<IMessageConfirmation>} - A promise that resolves to a message confirmation.
|
|
57
|
+
*/
|
|
58
|
+
send(message: IMessage<OcppRequest | OcppResponse | OcppError>, payload?: OcppRequest | OcppResponse | OcppError, state?: MessageState): Promise<IMessageConfirmation>;
|
|
59
|
+
/**
|
|
60
|
+
* Shuts down the sender by closing the client.
|
|
61
|
+
*
|
|
62
|
+
* @return {Promise<void>} A promise that resolves when the client is closed.
|
|
63
|
+
*/
|
|
64
|
+
shutdown(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Protected Methods
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Connect to RabbitMQ with retry logic.
|
|
70
|
+
* This method will keep trying to connect until successful, unless aborted.
|
|
71
|
+
*
|
|
72
|
+
* @param {AbortSignal} [abortSignal] - Optional abort signal to stop retrying.
|
|
73
|
+
* @return {Promise<amqplib.Channel>} A promise that resolves to the AMQP channel.
|
|
74
|
+
*/
|
|
75
|
+
protected _connectWithRetry(abortSignal?: AbortSignal): Promise<amqplib.Channel>;
|
|
76
|
+
private _startReconnectInterval;
|
|
77
|
+
private _onCircuitBreakerStateChange;
|
|
78
|
+
/**
|
|
79
|
+
* Setup listeners for connection and channel events.
|
|
80
|
+
* This will handle disconnections and errors.
|
|
81
|
+
* Ensures listeners are not attached multiple times to the same connection.
|
|
82
|
+
*/
|
|
83
|
+
private _setupConnectionListeners;
|
|
84
|
+
/**
|
|
85
|
+
* Handle RabbitMQ disconnection.
|
|
86
|
+
* This method will attempt to reconnect to RabbitMQ when the connection is lost.
|
|
87
|
+
* Debounces concurrent reconnects.
|
|
88
|
+
*/
|
|
89
|
+
private _handleDisconnect;
|
|
90
|
+
}
|