@autofleet/rabbit 4.0.2-beta.8 → 4.1.0-beta.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/coverage/clover.xml +669 -0
- package/coverage/coverage-final.json +9 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +131 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/index.html +131 -0
- package/coverage/lcov-report/src/index.ts.html +3826 -0
- package/coverage/lcov-report/src/lib/celery.ts.html +352 -0
- package/coverage/lcov-report/src/lib/consts.ts.html +142 -0
- package/coverage/lcov-report/src/lib/index.html +191 -0
- package/coverage/lcov-report/src/lib/rabbitError.ts.html +103 -0
- package/coverage/lcov-report/src/lib/redis.ts.html +133 -0
- package/coverage/lcov-report/src/lib/types.ts.html +268 -0
- package/coverage/lcov-report/src/lib/utils.ts.html +142 -0
- package/coverage/lcov-report/src/logger.ts.html +100 -0
- package/coverage/lcov.info +1076 -0
- package/dist/index.d.ts +19 -33
- package/dist/index.js +217 -341
- package/dist/lib/celery.js +0 -1
- package/dist/lib/consts.d.ts +2 -10
- package/dist/lib/consts.js +3 -12
- package/dist/lib/rabbitError.js +0 -1
- package/dist/lib/redis.js +0 -1
- package/dist/lib/types.d.ts +0 -8
- package/dist/lib/types.js +0 -1
- package/dist/lib/utils.d.ts +0 -5
- package/dist/lib/utils.js +1 -9
- package/dist/logger.js +0 -1
- package/dist/{mock/index.d.ts → mock.d.ts} +1 -1
- package/dist/{mock/index.js → mock.js} +1 -2
- package/package.json +16 -23
- package/src/index.ts +243 -417
- package/src/lib/consts.ts +2 -11
- package/src/lib/types.ts +0 -10
- package/src/lib/utils.ts +0 -10
- package/src/{mock/index.ts → mock.ts} +2 -2
- package/tsconfig.json +2 -2
- package/dist/index.js.map +0 -1
- package/dist/lib/celery.js.map +0 -1
- package/dist/lib/consts.js.map +0 -1
- package/dist/lib/rabbitError.js.map +0 -1
- package/dist/lib/redis.js.map +0 -1
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/utils.js.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/mock/index.js.map +0 -1
- package/dist/mock/vitest.d.ts +0 -13
- package/dist/mock/vitest.js +0 -18
- package/dist/mock/vitest.js.map +0 -1
- package/src/mock/vitest.ts +0 -24
- package/tsconfig.build.json +0 -5
- package/vitest.config.ts +0 -17
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars
|
|
1
|
+
/* eslint-disable no-empty,consistent-return,no-async-promise-executor,@typescript-eslint/no-unused-vars */
|
|
2
|
+
|
|
2
3
|
import { EventEmitter, once } from 'events';
|
|
3
4
|
import { promisify } from 'util';
|
|
4
5
|
import moment from 'moment';
|
|
@@ -9,19 +10,19 @@ import {
|
|
|
9
10
|
import {
|
|
10
11
|
ConfirmChannel, ConsumeMessage, Options, Replies,
|
|
11
12
|
} from 'amqplib';
|
|
12
|
-
import { backOff } from 'exponential-backoff';
|
|
13
13
|
import {
|
|
14
14
|
getCurrentPayload, newTrace, traceTypes, createOrSetRabbitTrace, outbreak,
|
|
15
15
|
} from '@autofleet/zehut';
|
|
16
16
|
import { randomUUID } from 'node:crypto';
|
|
17
|
+
import axios from 'axios';
|
|
17
18
|
import logger from './logger';
|
|
18
19
|
import RabbitError from './lib/rabbitError';
|
|
19
20
|
import getRedisInstance, { RedisConfig } from './lib/redis';
|
|
20
|
-
import {
|
|
21
|
-
assertExchangeFanout, getAssertVhostExponentialBackoffConfig, rand, wrapSetImmediate,
|
|
22
|
-
} from './lib/utils';
|
|
21
|
+
import { assertExchangeFanout, rand, wrapSetImmediate } from './lib/utils';
|
|
23
22
|
import {
|
|
24
23
|
AUTOMATION_ID_HEADER,
|
|
24
|
+
CONNECTION_CREATED_CONST,
|
|
25
|
+
CONNECTION_FAILED_CONST,
|
|
25
26
|
DEFAULT_LOCK_TIMEOUT,
|
|
26
27
|
DEFAULT_OPTIONS,
|
|
27
28
|
RETRY_HEADER,
|
|
@@ -40,7 +41,6 @@ import {
|
|
|
40
41
|
CONSUMER_DEFAULT_OPTIONS,
|
|
41
42
|
QueueSetupPromisesDictionary,
|
|
42
43
|
AssertExchangePromisesDictionary,
|
|
43
|
-
ConnectionData,
|
|
44
44
|
} from './lib/types';
|
|
45
45
|
|
|
46
46
|
// const debug = nodeDebug('af-rabbitmq')
|
|
@@ -48,12 +48,6 @@ const debug = logger.debug.bind(logger);
|
|
|
48
48
|
|
|
49
49
|
const PUBLISH_TIMEOUT = 1000 * 10;
|
|
50
50
|
|
|
51
|
-
// TODO: [QUORUM-PHASE-3] Delete this env var
|
|
52
|
-
const {
|
|
53
|
-
DISABLE_QUORUM_QUEUES_CONSUME,
|
|
54
|
-
DISABLE_QUORUM_QUEUES_PUBLISH,
|
|
55
|
-
} = process.env;
|
|
56
|
-
|
|
57
51
|
export interface IAfRabbitMq {
|
|
58
52
|
ack: any;
|
|
59
53
|
nack: any;
|
|
@@ -95,13 +89,11 @@ type newChannelOpts = {
|
|
|
95
89
|
name?: string;
|
|
96
90
|
onClose?: null | ((args: any | null) => void);
|
|
97
91
|
options?: CreateChannelOpts | undefined;
|
|
98
|
-
connection: ConnectionData;
|
|
99
92
|
};
|
|
100
93
|
|
|
101
94
|
type assertChannelOpts = {
|
|
102
95
|
channelName?: string;
|
|
103
96
|
force?: boolean;
|
|
104
|
-
connection: ConnectionData;
|
|
105
97
|
}
|
|
106
98
|
|
|
107
99
|
type AfConsumer = {
|
|
@@ -154,15 +146,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
154
146
|
|
|
155
147
|
RECONNECT_MSG = 'rabbit: connection disconnect - reconnecting';
|
|
156
148
|
|
|
157
|
-
|
|
149
|
+
channel: ChannelWrapper | null;
|
|
158
150
|
|
|
159
151
|
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
160
152
|
|
|
161
153
|
blockReconnect: boolean | null | undefined
|
|
162
154
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
consumeConnection: ConnectionData
|
|
155
|
+
connection: AmqpConnectionManager | null | undefined
|
|
166
156
|
|
|
167
157
|
em: EventEmitter;
|
|
168
158
|
|
|
@@ -176,33 +166,28 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
176
166
|
|
|
177
167
|
assertExchangePromises: AssertExchangePromisesDictionary;
|
|
178
168
|
|
|
179
|
-
options: AfRabbitOptions;
|
|
169
|
+
options: AfRabbitOptions | undefined;
|
|
180
170
|
|
|
181
171
|
redisClient: any;
|
|
182
172
|
|
|
183
173
|
redisLock?: RedisLockType;
|
|
184
174
|
|
|
185
175
|
/** Array of consumers tags used for canceling consumption */
|
|
186
|
-
consumersTags:
|
|
176
|
+
consumersTags: Array<[ConfirmChannel, string]>;
|
|
187
177
|
|
|
188
|
-
private
|
|
178
|
+
private consumers: Array<AfConsumer> = [];
|
|
189
179
|
|
|
190
|
-
private
|
|
180
|
+
private isVHostExist = false;
|
|
191
181
|
|
|
192
|
-
|
|
182
|
+
// TODO: DELETE OLD PROPERTIES
|
|
193
183
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
// TODO:[QUORUM-PHASE-3] Delete the old properties that we use for the old consumers and publishers
|
|
197
|
-
oldPublishChannel: ChannelWrapper | null;
|
|
184
|
+
oldChannel: ChannelWrapper | null;
|
|
198
185
|
|
|
199
186
|
oldPublishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
200
187
|
|
|
201
188
|
oldBlockReconnect: boolean | null | undefined
|
|
202
189
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
oldConsumeConnection: ConnectionData
|
|
190
|
+
oldConnection: AmqpConnectionManager | null | undefined
|
|
206
191
|
|
|
207
192
|
oldEm: EventEmitter;
|
|
208
193
|
|
|
@@ -216,41 +201,32 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
216
201
|
|
|
217
202
|
oldAssertExchangePromises: AssertExchangePromisesDictionary;
|
|
218
203
|
|
|
219
|
-
oldConsumersTags:
|
|
204
|
+
oldConsumersTags: Array<[ConfirmChannel, string]>;
|
|
220
205
|
|
|
221
|
-
private
|
|
206
|
+
private oldConsumers: Array<AfConsumer> = [];
|
|
222
207
|
|
|
223
208
|
constructor(options: AfRabbitOptions = {}, redisConfig?: RedisConfig) {
|
|
224
209
|
this.em = new EventEmitter();
|
|
225
|
-
this.
|
|
210
|
+
this.channel = null;
|
|
226
211
|
this.publishChannelSetupPromise = null;
|
|
227
|
-
this.
|
|
228
|
-
amqpConnection: null,
|
|
229
|
-
creatingConnection: false,
|
|
230
|
-
connectionCreatedEventName: 'publishConnectionCreated',
|
|
231
|
-
connectionFailedEventName: 'publishConnectionFailed',
|
|
232
|
-
blockReconnect: false,
|
|
233
|
-
};
|
|
234
|
-
this.consumeConnection = {
|
|
235
|
-
amqpConnection: null,
|
|
236
|
-
creatingConnection: false,
|
|
237
|
-
connectionCreatedEventName: 'consumeConnectionCreated',
|
|
238
|
-
connectionFailedEventName: 'consumeConnectionFailed',
|
|
239
|
-
blockReconnect: false,
|
|
240
|
-
};
|
|
212
|
+
this.connection = null;
|
|
241
213
|
this.creatingConnection = false;
|
|
242
214
|
this.exchanges = {};
|
|
243
215
|
this.queues = {};
|
|
244
216
|
this.queueSetupPromises = {};
|
|
245
217
|
this.assertExchangePromises = {};
|
|
246
|
-
this.
|
|
218
|
+
this.consumers = [];
|
|
247
219
|
this.options = options;
|
|
248
220
|
|
|
221
|
+
if (!options?.vhost) {
|
|
222
|
+
this.options.vhost = process.env.VHOST_NAME || 'quorum-vhost';
|
|
223
|
+
}
|
|
224
|
+
|
|
249
225
|
this.redisClient = redisConfig && getRedisInstance(redisConfig);
|
|
250
226
|
if (this.redisClient) {
|
|
251
227
|
this.redisLock = promisify(RedisLock(this.redisClient)) as RedisLockType;
|
|
252
228
|
}
|
|
253
|
-
this.consumersTags =
|
|
229
|
+
this.consumersTags = [];
|
|
254
230
|
logger.info(`rabbit: [gracefully-shutdown] adding gracefully shutdown for process.pid ${process.pid}`);
|
|
255
231
|
if (!this.options?.dontGracefulShutdown) {
|
|
256
232
|
process.on('SIGTERM', async () => {
|
|
@@ -261,85 +237,72 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
261
237
|
});
|
|
262
238
|
}
|
|
263
239
|
|
|
264
|
-
// TODO:
|
|
240
|
+
// TODO: DELETE OLD PROPERTIES
|
|
265
241
|
this.oldEm = new EventEmitter();
|
|
266
|
-
this.
|
|
242
|
+
this.oldChannel = null;
|
|
267
243
|
this.oldPublishChannelSetupPromise = null;
|
|
268
|
-
this.
|
|
269
|
-
amqpConnection: null,
|
|
270
|
-
creatingConnection: false,
|
|
271
|
-
connectionCreatedEventName: 'oldPublishConnectionCreated',
|
|
272
|
-
connectionFailedEventName: 'oldPublishConnectionFailed',
|
|
273
|
-
blockReconnect: false,
|
|
274
|
-
};
|
|
275
|
-
this.oldConsumeConnection = {
|
|
276
|
-
amqpConnection: null,
|
|
277
|
-
creatingConnection: false,
|
|
278
|
-
connectionCreatedEventName: 'oldConsumeConnectionCreated',
|
|
279
|
-
connectionFailedEventName: 'oldConsumeConnectionFailed',
|
|
280
|
-
blockReconnect: false,
|
|
281
|
-
};
|
|
244
|
+
this.oldConnection = null;
|
|
282
245
|
this.oldCreatingConnection = false;
|
|
283
246
|
this.oldExchanges = {};
|
|
284
247
|
this.oldQueues = {};
|
|
285
248
|
this.oldQueueSetupPromises = {};
|
|
286
249
|
this.oldAssertExchangePromises = {};
|
|
287
|
-
this.
|
|
288
|
-
this.oldConsumersTags =
|
|
250
|
+
this.oldConsumers = [];
|
|
251
|
+
this.oldConsumersTags = [];
|
|
289
252
|
}
|
|
290
253
|
|
|
291
254
|
private assertVHost = async () => {
|
|
292
|
-
if (this.
|
|
255
|
+
if (this.isVHostExist) {
|
|
293
256
|
return;
|
|
294
257
|
}
|
|
295
258
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const headers = {
|
|
300
|
-
Authorization: `Basic ${credentials}`,
|
|
301
|
-
'Content-Type': 'application/json',
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
const rabbitHost = `http://${(this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
|
|
259
|
+
if (!this.options?.vhost) {
|
|
260
|
+
throw new Error('vhost is not defined');
|
|
261
|
+
}
|
|
305
262
|
|
|
306
|
-
const
|
|
263
|
+
const { vhost } = this.options;
|
|
264
|
+
const auth = {
|
|
265
|
+
auth: {
|
|
266
|
+
username: process.env.RABBITMQ_USERNAME || 'guest',
|
|
267
|
+
password: process.env.RABBITMQ_PASSWORD || 'guest',
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
const rabbitHost = `http://${(this.options.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost').split(':')[0]}:15672`;
|
|
307
271
|
|
|
308
272
|
try {
|
|
309
|
-
const response = await
|
|
310
|
-
method: 'GET',
|
|
311
|
-
headers,
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
if (response.status === 200) {
|
|
315
|
-
this.doesVHostExist = true;
|
|
316
|
-
logger.info('Vhost exists', { vhost: this.vhost });
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
273
|
+
const response = await axios.get(`${rabbitHost}/api/vhosts/${encodeURIComponent(vhost)}`, auth);
|
|
319
274
|
|
|
320
|
-
if (response.status !==
|
|
321
|
-
|
|
322
|
-
throw new RabbitError('Failed to check vhost');
|
|
275
|
+
if (response.status !== 200) {
|
|
276
|
+
throw new Error('Failed to check vhost');
|
|
323
277
|
}
|
|
324
278
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
279
|
+
this.isVHostExist = true;
|
|
280
|
+
logger.info('Vhost exists', { vhost });
|
|
281
|
+
} catch (error) {
|
|
282
|
+
// If the vhost does not exist, create it
|
|
283
|
+
if (error instanceof axios.AxiosError && error.response?.status === 404) {
|
|
284
|
+
try {
|
|
285
|
+
await axios.put(
|
|
286
|
+
`${rabbitHost}/api/vhosts/${encodeURIComponent(vhost)}`,
|
|
287
|
+
{
|
|
288
|
+
default_queue_type: 'quorum',
|
|
289
|
+
},
|
|
290
|
+
auth,
|
|
291
|
+
);
|
|
330
292
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
293
|
+
this.isVHostExist = true;
|
|
294
|
+
logger.info('Vhost created', { vhost: this.options?.vhost });
|
|
295
|
+
return;
|
|
296
|
+
} catch (createError) {
|
|
297
|
+
logger.error('Failed to create vhost', { error: createError });
|
|
298
|
+
throw createError;
|
|
299
|
+
}
|
|
334
300
|
}
|
|
335
301
|
|
|
336
|
-
|
|
337
|
-
logger.info('Vhost created', { vhost: this.vhost });
|
|
338
|
-
} catch (error) {
|
|
339
|
-
logger.error('Failed to check or create vhost', { error });
|
|
302
|
+
logger.error('Failed to check vhost', { error });
|
|
340
303
|
throw error;
|
|
341
304
|
}
|
|
342
|
-
}
|
|
305
|
+
}
|
|
343
306
|
|
|
344
307
|
private shouldConsumeMessageByTimestamp = async (msg: ConsumeMessageOrNull) => {
|
|
345
308
|
if (msg) {
|
|
@@ -388,13 +351,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
388
351
|
if (
|
|
389
352
|
!skipRetry
|
|
390
353
|
&& (
|
|
391
|
-
!msg.properties.headers
|
|
354
|
+
!msg.properties.headers[RETRY_HEADER]
|
|
392
355
|
|| parseInt(msg.properties.headers[RETRY_HEADER], 10) < options.retries
|
|
393
356
|
)
|
|
394
357
|
) {
|
|
395
358
|
await this.sendToQueue(queue, RabbitMq.parseMsg(msg).content, options, {
|
|
396
359
|
...msg.properties.headers,
|
|
397
|
-
[RETRY_HEADER]: msg.properties.headers
|
|
360
|
+
[RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
|
|
398
361
|
? msg.properties.headers[RETRY_HEADER] + 1
|
|
399
362
|
: 1,
|
|
400
363
|
});
|
|
@@ -402,7 +365,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
402
365
|
const deadQueue = `${queue}-dead`;
|
|
403
366
|
await this.sendToQueue(deadQueue, RabbitMq.parseMsg(msg).content, deadQueueOptions, {
|
|
404
367
|
...msg.properties.headers,
|
|
405
|
-
[RETRY_HEADER]: msg.properties.headers
|
|
368
|
+
[RETRY_HEADER]: msg.properties.headers[RETRY_HEADER]
|
|
406
369
|
? msg.properties.headers[RETRY_HEADER] + 1
|
|
407
370
|
: 1,
|
|
408
371
|
});
|
|
@@ -416,38 +379,30 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
416
379
|
}
|
|
417
380
|
}
|
|
418
381
|
|
|
419
|
-
async getConnection(
|
|
382
|
+
async getConnection() {
|
|
420
383
|
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
421
|
-
|
|
422
|
-
amqpConnection: connectionLocal,
|
|
423
|
-
creatingConnection,
|
|
424
|
-
connectionCreatedEventName,
|
|
425
|
-
connectionFailedEventName,
|
|
426
|
-
blockReconnect,
|
|
427
|
-
} = connection;
|
|
428
|
-
|
|
429
|
-
if (blockReconnect) {
|
|
384
|
+
if (this.blockReconnect) {
|
|
430
385
|
debug('rabbit: block reconnect');
|
|
431
386
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
432
387
|
// @ts-ignore
|
|
433
388
|
return resolve();
|
|
434
389
|
}
|
|
435
|
-
if (
|
|
436
|
-
if (this.options?.disableReconnect ||
|
|
390
|
+
if (this.connection !== null) {
|
|
391
|
+
if (this.options?.disableReconnect || this.connection?.isConnected()) {
|
|
437
392
|
debug('rabbit: connection - is connected');
|
|
438
393
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
439
394
|
// @ts-ignore
|
|
440
|
-
return resolve(
|
|
395
|
+
return resolve(this.connection);
|
|
441
396
|
}
|
|
442
397
|
debug('rabbit: connection - reconnecting');
|
|
443
398
|
}
|
|
444
|
-
if (creatingConnection) {
|
|
399
|
+
if (this.creatingConnection) {
|
|
445
400
|
debug('rabbit: creating connection emi');
|
|
446
|
-
this.em.once(
|
|
447
|
-
this.em.once(
|
|
401
|
+
this.em.once(CONNECTION_CREATED_CONST, resolve);
|
|
402
|
+
this.em.once(CONNECTION_FAILED_CONST, reject);
|
|
448
403
|
return;
|
|
449
404
|
}
|
|
450
|
-
|
|
405
|
+
this.creatingConnection = true;
|
|
451
406
|
let isResolved = false;
|
|
452
407
|
|
|
453
408
|
// It is import to use it as a function and not as a variable
|
|
@@ -459,72 +414,65 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
459
414
|
const host = this.options?.rabbitHost || process.env.RABBITMQ_SERVICE_HOST || 'localhost';
|
|
460
415
|
|
|
461
416
|
debug('rabbit: creating connection', { host, userName, HEARTBEAT });
|
|
462
|
-
return [`amqp://${userName}:${password}@${host}/${this.vhost}?heartbeat=${HEARTBEAT}`];
|
|
417
|
+
return [`amqp://${userName}:${password}@${host}/${this.options?.vhost}?heartbeat=${HEARTBEAT}`];
|
|
463
418
|
};
|
|
464
419
|
|
|
465
420
|
const defaultUrls = findServers();
|
|
466
|
-
const
|
|
421
|
+
const connection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
467
422
|
findServers,
|
|
468
423
|
});
|
|
469
424
|
|
|
470
|
-
connection
|
|
471
|
-
|
|
425
|
+
this.connection = connection;
|
|
426
|
+
this.connection.on('error', (err) => {
|
|
472
427
|
logger.error('rabbit: connection error', { err });
|
|
473
428
|
if (!isResolved) {
|
|
474
429
|
isResolved = true;
|
|
475
430
|
reject(err);
|
|
476
|
-
this.em.emit(
|
|
431
|
+
this.em.emit(CONNECTION_FAILED_CONST, err);
|
|
477
432
|
}
|
|
478
433
|
});
|
|
479
434
|
|
|
480
|
-
|
|
481
|
-
this.consumersTags
|
|
482
|
-
|
|
483
|
-
err.url = this.maskURL(err.url);
|
|
484
|
-
}
|
|
485
|
-
logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.vhost });
|
|
435
|
+
this.connection.on('connectFailed', (err) => {
|
|
436
|
+
this.consumersTags = [];
|
|
437
|
+
logger.error('rabbit: connection connectFailed', { err, advice: 'Check if the vhost exist', vhost: this.options?.vhost });
|
|
486
438
|
if (!isResolved) {
|
|
487
439
|
isResolved = true;
|
|
488
440
|
reject(err);
|
|
489
|
-
this.em.emit(
|
|
441
|
+
this.em.emit(CONNECTION_FAILED_CONST, err);
|
|
490
442
|
}
|
|
491
443
|
});
|
|
492
444
|
|
|
493
|
-
|
|
494
|
-
this.
|
|
445
|
+
this.connection.on('disconnect', ({ err }) => {
|
|
446
|
+
// this.channel = null;
|
|
447
|
+
this.consumersTags = [];
|
|
495
448
|
debug('rabbit: connection closed');
|
|
496
449
|
if (this.options?.disableReconnect) {
|
|
497
450
|
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
498
|
-
|
|
451
|
+
this.blockReconnect = true;
|
|
499
452
|
} else {
|
|
500
453
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
501
454
|
}
|
|
502
455
|
});
|
|
503
456
|
|
|
504
|
-
|
|
457
|
+
this.connection.once('connect', async () => {
|
|
505
458
|
debug('rabbit: connection established');
|
|
506
|
-
|
|
507
|
-
this.em.emit(
|
|
459
|
+
this.creatingConnection = false;
|
|
460
|
+
this.em.emit(CONNECTION_CREATED_CONST, connection);
|
|
508
461
|
isResolved = true;
|
|
509
|
-
resolve(
|
|
462
|
+
resolve(connection);
|
|
510
463
|
});
|
|
511
464
|
});
|
|
512
465
|
}
|
|
513
466
|
|
|
514
|
-
async getNewChannel({
|
|
515
|
-
|
|
516
|
-
onClose = null,
|
|
517
|
-
options = {},
|
|
518
|
-
connection,
|
|
519
|
-
}: newChannelOpts) {
|
|
520
|
-
let localConnection!: AmqpConnectionManager;
|
|
467
|
+
async getNewChannel({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
|
|
468
|
+
let connection!: AmqpConnectionManager;
|
|
521
469
|
try {
|
|
522
|
-
|
|
470
|
+
connection = await this.getConnection();
|
|
523
471
|
} catch (e) {
|
|
524
472
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
525
473
|
throw e;
|
|
526
474
|
}
|
|
527
|
-
const channel =
|
|
475
|
+
const channel = connection.createChannel({ ...options });
|
|
528
476
|
once(channel, 'close').then((args) => {
|
|
529
477
|
logger.error(`rabbit: channel ${name} closed`);
|
|
530
478
|
onClose?.(args);
|
|
@@ -539,21 +487,19 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
539
487
|
}
|
|
540
488
|
}
|
|
541
489
|
|
|
542
|
-
async assertChannel({ force = false
|
|
490
|
+
async assertChannel({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
|
|
543
491
|
if (!this.publishChannelSetupPromise) {
|
|
544
492
|
this.publishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
545
|
-
if (this.
|
|
546
|
-
return resolve(this.
|
|
493
|
+
if (this.channel && !force) {
|
|
494
|
+
return resolve(this.channel);
|
|
547
495
|
}
|
|
548
496
|
|
|
549
497
|
try {
|
|
550
|
-
const channel = await this.getNewChannel({
|
|
498
|
+
const channel = await this.getNewChannel({});
|
|
551
499
|
channel.on('error', (err) => {
|
|
552
500
|
logger.error('rabbit: channel error', { err });
|
|
553
501
|
});
|
|
554
|
-
|
|
555
|
-
this.publishChannel = channel;
|
|
556
|
-
}
|
|
502
|
+
this.channel = channel;
|
|
557
503
|
resolve(channel);
|
|
558
504
|
} catch (e) {
|
|
559
505
|
reject(e);
|
|
@@ -563,8 +509,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
563
509
|
return this.publishChannelSetupPromise;
|
|
564
510
|
}
|
|
565
511
|
|
|
566
|
-
async assertExchange(exchangeName: string,
|
|
567
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
512
|
+
async assertExchange(exchangeName: string, options?: any) {
|
|
513
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
568
514
|
|
|
569
515
|
if (this.exchanges[exchangeName]) {
|
|
570
516
|
delete this.assertExchangePromises[exchangeName];
|
|
@@ -582,17 +528,17 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
582
528
|
|
|
583
529
|
async getQueueLength(queue: string) {
|
|
584
530
|
RabbitMq.validateName('queue', queue);
|
|
585
|
-
const {
|
|
531
|
+
const { channel } = this;
|
|
586
532
|
if (!channel) {
|
|
587
|
-
throw new
|
|
533
|
+
throw new Error('channel is not defined');
|
|
588
534
|
}
|
|
589
|
-
debug('rabbit: getting queue length', { queue, connected: this.
|
|
535
|
+
debug('rabbit: getting queue length', { queue, connected: this.connection?.isConnected() });
|
|
590
536
|
return channel?.checkQueue(queue);
|
|
591
537
|
}
|
|
592
538
|
|
|
593
|
-
private async deleteQueue(queue: string
|
|
539
|
+
private async deleteQueue(queue: string) {
|
|
594
540
|
RabbitMq.validateName('queue', queue);
|
|
595
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
541
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
596
542
|
logger.info('rabbit: deleting queue', { queue });
|
|
597
543
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
598
544
|
debug('queue deleted', deleteQueueRes);
|
|
@@ -600,14 +546,13 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
600
546
|
}
|
|
601
547
|
|
|
602
548
|
async bindQueue(queue: string, exchange: string) {
|
|
603
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
549
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
604
550
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
605
551
|
return channel.bindQueue(queue, exchange, '');
|
|
606
552
|
}
|
|
607
553
|
|
|
608
554
|
async setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
609
555
|
let queue: Replies.AssertQueue;
|
|
610
|
-
const connection = this.publishConnection;
|
|
611
556
|
const localeOptions = {
|
|
612
557
|
...options,
|
|
613
558
|
durable: true,
|
|
@@ -618,7 +563,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
618
563
|
},
|
|
619
564
|
};
|
|
620
565
|
try {
|
|
621
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
566
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
622
567
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
623
568
|
await channel.addSetup(async (setupChannel: ConfirmChannel) => {
|
|
624
569
|
await setupChannel.assertQueue(queueName, localeOptions);
|
|
@@ -629,8 +574,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
629
574
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
630
575
|
if (!this.options?.dontRetryAssert) {
|
|
631
576
|
debug('retrying assertQueue', { queueName });
|
|
632
|
-
const channel = await this.assertChannel({ force: true
|
|
633
|
-
await this.deleteQueue(queueName
|
|
577
|
+
const channel = await this.assertChannel({ force: true });
|
|
578
|
+
await this.deleteQueue(queueName);
|
|
634
579
|
|
|
635
580
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
636
581
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
@@ -645,7 +590,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
645
590
|
return queue;
|
|
646
591
|
}
|
|
647
592
|
|
|
648
|
-
// TODO:
|
|
593
|
+
// TODO: Can be deleted after deleting the old consumers
|
|
649
594
|
static shouldUseQuorum(queueName: string): boolean {
|
|
650
595
|
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
651
596
|
|
|
@@ -677,10 +622,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
677
622
|
}
|
|
678
623
|
|
|
679
624
|
private saveConsumer(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
680
|
-
const isConsumerExist:
|
|
625
|
+
const isConsumerExist :boolean = this.consumers.some((consumer) => consumer.queue === queue);
|
|
681
626
|
if (!isConsumerExist) {
|
|
682
627
|
logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
683
|
-
this.
|
|
628
|
+
this.consumers.push({
|
|
684
629
|
queue,
|
|
685
630
|
callback,
|
|
686
631
|
options,
|
|
@@ -690,24 +635,21 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
690
635
|
|
|
691
636
|
// Used by the microservices to consume messages from the queue
|
|
692
637
|
async consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
if (options?.isQuorumQueue !== false && DISABLE_QUORUM_QUEUES_CONSUME !== 'true') {
|
|
697
|
-
this.saveConsumer(queue, callback, options);
|
|
698
|
-
const backoffConfig = getAssertVhostExponentialBackoffConfig();
|
|
699
|
-
await backOff(() => this.assertVHost(), backoffConfig);
|
|
638
|
+
// TODO: USE THE IMPLEMENTATION OF THE NEW CONSUME AND DELETE THE NEW AND OLD
|
|
639
|
+
if (options?.isQuorumQueue !== false) {
|
|
640
|
+
await this.assertVHost();
|
|
700
641
|
await this.consumeNew(queue, callback, options);
|
|
701
642
|
}
|
|
643
|
+
|
|
702
644
|
await this.consumeOld(queue, callback, options);
|
|
703
645
|
}
|
|
704
646
|
|
|
705
|
-
// TODO:
|
|
647
|
+
// TODO: DELETE
|
|
706
648
|
async consumeNew(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
707
649
|
await this.consumeFromRabbit(queue, callback, options);
|
|
708
650
|
}
|
|
709
651
|
|
|
710
|
-
// TODO:
|
|
652
|
+
// TODO: DELETE
|
|
711
653
|
async consumeOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
712
654
|
await this.consumeFromRabbitOld(queue, callback, options);
|
|
713
655
|
}
|
|
@@ -735,17 +677,18 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
735
677
|
private async consumeFromRabbit(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
736
678
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
737
679
|
RabbitMq.validateName('queue', queue);
|
|
680
|
+
this.saveConsumer(queue, callback, options);
|
|
738
681
|
const uniqueId = randomUUID();
|
|
739
682
|
const {
|
|
740
683
|
limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace,
|
|
741
684
|
} = optionsWithDefaults;
|
|
742
685
|
if (useConsumeWithLock) {
|
|
743
686
|
if (!this.redisLock) {
|
|
744
|
-
throw new
|
|
687
|
+
throw new Error('Usage of consumeWithLock requires RedisInstance');
|
|
745
688
|
}
|
|
746
689
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
747
690
|
}
|
|
748
|
-
const channel = await this.getNewChannel({
|
|
691
|
+
const channel = await this.getNewChannel({});
|
|
749
692
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
750
693
|
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
751
694
|
await confirmChannel.prefetch(limit, false);
|
|
@@ -756,9 +699,9 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
756
699
|
return null;
|
|
757
700
|
}
|
|
758
701
|
|
|
759
|
-
const traceId = msg.properties.headers
|
|
760
|
-
const userId = msg.properties.headers
|
|
761
|
-
const automationId = msg.properties.headers
|
|
702
|
+
const traceId = msg.properties.headers[TRACING_HEADER];
|
|
703
|
+
const userId = msg.properties.headers[USER_TRACING_HEADER];
|
|
704
|
+
const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
|
|
762
705
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
763
706
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
764
707
|
const trace = newTrace(traceTypes.RABBIT);
|
|
@@ -830,7 +773,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
830
773
|
logger.error(`rabbit: failed to consume from queue ${queue}`);
|
|
831
774
|
} else {
|
|
832
775
|
logger.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
833
|
-
this.consumersTags.
|
|
776
|
+
this.consumersTags.push([confirmChannel, consumerTag]);
|
|
834
777
|
}
|
|
835
778
|
});
|
|
836
779
|
}
|
|
@@ -841,15 +784,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
841
784
|
RabbitMq.validateName('exchange', exchange);
|
|
842
785
|
RabbitMq.validateName('queue', queue);
|
|
843
786
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
844
|
-
|
|
845
|
-
|
|
787
|
+
if (options?.isQuorumQueue !== false) {
|
|
788
|
+
await this.assertVHost();
|
|
846
789
|
await this.saveConsumer(queue, callback, options);
|
|
847
|
-
const
|
|
848
|
-
await backOff(() => this.assertVHost(), backoffConfig);
|
|
849
|
-
const channel: ChannelWrapper = await this.getNewChannel({
|
|
850
|
-
name: `consume-exchange-${exchange}-queue-${queue}`,
|
|
851
|
-
connection: this.consumeConnection,
|
|
852
|
-
});
|
|
790
|
+
const channel: ChannelWrapper = await this.getNewChannel({ name: `consume-exchange-${exchange}-queue-${queue}` });
|
|
853
791
|
|
|
854
792
|
await channel.addSetup(async (c: ConfirmChannel) => {
|
|
855
793
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
@@ -866,12 +804,10 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
866
804
|
]);
|
|
867
805
|
});
|
|
868
806
|
}
|
|
869
|
-
|
|
807
|
+
|
|
808
|
+
// TODO: DELETE OLD
|
|
870
809
|
await this.saveConsumerOld(queue, callback, options);
|
|
871
|
-
const channelOld: ChannelWrapper = await this.getNewChannelOld({
|
|
872
|
-
name: `consume-exchange-${exchange}-queue-${queue}-old`,
|
|
873
|
-
connection: this.oldConsumeConnection,
|
|
874
|
-
});
|
|
810
|
+
const channelOld: ChannelWrapper = await this.getNewChannelOld({ name: `consume-exchange-${exchange}-queue-${queue}-old` });
|
|
875
811
|
await channelOld.addSetup(async (c: ConfirmChannel) => {
|
|
876
812
|
const assertExchange = await assertExchangeFanout(c, exchange);
|
|
877
813
|
await c.assertQueue(queue);
|
|
@@ -889,25 +825,25 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
889
825
|
}
|
|
890
826
|
|
|
891
827
|
// Used by the microservices to publish messages to the exchange
|
|
892
|
-
// TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
|
|
893
828
|
async publish(exchange: string, content: any, customHeaders?: any, isQuorumQueue = true) : Promise<boolean> {
|
|
894
|
-
|
|
829
|
+
// TODO: DELETE THE IF
|
|
830
|
+
if (isQuorumQueue) {
|
|
895
831
|
return wrapSetImmediate(async () => {
|
|
896
832
|
await this.assertVHost();
|
|
897
833
|
RabbitMq.validateName('exchange', exchange);
|
|
898
|
-
const channel: ChannelWrapper = await this.assertChannel(
|
|
899
|
-
await this.assertExchange(exchange
|
|
834
|
+
const channel: ChannelWrapper = await this.assertChannel();
|
|
835
|
+
await this.assertExchange(exchange);
|
|
900
836
|
await channel.publish(exchange, '',
|
|
901
837
|
Buffer.from(JSON.stringify(content)),
|
|
902
838
|
RabbitMq.getPublishOptions(customHeaders));
|
|
903
839
|
});
|
|
904
840
|
}
|
|
905
841
|
|
|
906
|
-
// TODO:
|
|
842
|
+
// TODO: DELETE THE OLD
|
|
907
843
|
return wrapSetImmediate(async () => {
|
|
908
844
|
RabbitMq.validateName('exchange', exchange);
|
|
909
|
-
const channel: ChannelWrapper = await this.assertChannelOld(
|
|
910
|
-
await this.assertExchangeOld(exchange
|
|
845
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
846
|
+
await this.assertExchangeOld(exchange);
|
|
911
847
|
await channel.publish(exchange, '',
|
|
912
848
|
Buffer.from(JSON.stringify(content)),
|
|
913
849
|
RabbitMq.getPublishOptions(customHeaders));
|
|
@@ -915,7 +851,6 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
915
851
|
}
|
|
916
852
|
|
|
917
853
|
// Used by the microservices to send messages to the queue
|
|
918
|
-
// TODO: [QUORUM-PHASE-3] isQuorumQueue flag is not needed anymore because all the queues are created as quorum queues
|
|
919
854
|
async sendToQueue(
|
|
920
855
|
queue: string,
|
|
921
856
|
content: any,
|
|
@@ -923,10 +858,11 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
923
858
|
customHeaders?: any,
|
|
924
859
|
isQuorumQueue = true,
|
|
925
860
|
): Promise<boolean | undefined> {
|
|
926
|
-
|
|
861
|
+
// TODO: DELETE THE IF AND THE OLD
|
|
862
|
+
if (isQuorumQueue) {
|
|
927
863
|
try {
|
|
928
864
|
await this.assertVHost();
|
|
929
|
-
await this.assertChannel(
|
|
865
|
+
await this.assertChannel();
|
|
930
866
|
} catch (e) {
|
|
931
867
|
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
932
868
|
throw e;
|
|
@@ -941,7 +877,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
941
877
|
}
|
|
942
878
|
|
|
943
879
|
try {
|
|
944
|
-
const res = await this.
|
|
880
|
+
const res = await this.channel?.sendToQueue(queue,
|
|
945
881
|
Buffer.from(JSON.stringify(content)),
|
|
946
882
|
RabbitMq.getPublishOptions(customHeaders));
|
|
947
883
|
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
@@ -953,97 +889,64 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
953
889
|
}
|
|
954
890
|
} else {
|
|
955
891
|
try {
|
|
956
|
-
await this.assertChannelOld(
|
|
892
|
+
await this.assertChannelOld();
|
|
957
893
|
} catch (e) {
|
|
958
894
|
logger.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
959
895
|
throw e;
|
|
960
896
|
}
|
|
897
|
+
}
|
|
961
898
|
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
899
|
+
try {
|
|
900
|
+
RabbitMq.validateName('queue', queue);
|
|
901
|
+
await this.assertQueueOld(queue, options);
|
|
902
|
+
} catch (e) {
|
|
903
|
+
logger.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
904
|
+
throw e;
|
|
905
|
+
}
|
|
969
906
|
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
}
|
|
907
|
+
try {
|
|
908
|
+
const res = await this.oldChannel?.sendToQueue(queue,
|
|
909
|
+
Buffer.from(JSON.stringify(content)),
|
|
910
|
+
RabbitMq.getPublishOptions(customHeaders));
|
|
911
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
912
|
+
return res;
|
|
913
|
+
} catch (e) {
|
|
914
|
+
const isConnected = await this.isConnected();
|
|
915
|
+
logger.error(`rabbit sendToQueue: failed to send to queue ${queue}, isConnected: ${isConnected}`, { e });
|
|
916
|
+
throw e;
|
|
981
917
|
}
|
|
982
918
|
}
|
|
983
919
|
|
|
984
|
-
async isConnected(): Promise<boolean> {
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
if (!
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
const [consumeConnection, publishConnection] = await Promise.all([
|
|
991
|
-
this.getConnection(this.consumeConnection),
|
|
992
|
-
this.getConnection(this.publishConnection),
|
|
993
|
-
]);
|
|
994
|
-
isConnected = consumeConnection.isConnected() && publishConnection.isConnected();
|
|
995
|
-
if (!isConnected) {
|
|
996
|
-
logger.error('rabbit: isConnected - false');
|
|
997
|
-
return false;
|
|
998
|
-
}
|
|
999
|
-
try {
|
|
1000
|
-
const unRegisteredConsumers = this.consumersToRegister.filter((c: AfConsumer) => {
|
|
1001
|
-
const doesConsumerExist = this.consumersTags.get(c.queue);
|
|
1002
|
-
return !doesConsumerExist;
|
|
1003
|
-
});
|
|
1004
|
-
|
|
1005
|
-
if (unRegisteredConsumers.length > 0) {
|
|
1006
|
-
const queueNames = unRegisteredConsumers.map((c) => c.queue);
|
|
1007
|
-
|
|
1008
|
-
logger.error('rabbit: found unregistered consumers for queues', {
|
|
1009
|
-
count: queueNames.length,
|
|
1010
|
-
queues: queueNames,
|
|
1011
|
-
});
|
|
1012
|
-
throw new RabbitError('Found unregistered consumers');
|
|
1013
|
-
}
|
|
1014
|
-
const channel: ChannelWrapper = await this.assertChannel({ connection: this.publishConnection });
|
|
1015
|
-
await channel.waitForConnect();
|
|
1016
|
-
|
|
1017
|
-
await Promise.all(
|
|
1018
|
-
this.consumersToRegister.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
1019
|
-
);
|
|
1020
|
-
} catch (e: RabbitError | any) {
|
|
1021
|
-
logger.error('rabbit: isConnected - false', { msg: e.message });
|
|
1022
|
-
return false;
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
// TODO:[QUORUM-PHASE-3] Delete the old is connected and adjust the return value to return true
|
|
1026
|
-
isConnected = await this.isConnectedOld();
|
|
920
|
+
async isConnected() : Promise<boolean> {
|
|
921
|
+
const connection = await this.getConnection();
|
|
922
|
+
const isConnected = connection.isConnected();
|
|
923
|
+
if (!isConnected) {
|
|
924
|
+
logger.error('rabbit: isConnected - false');
|
|
925
|
+
return false;
|
|
1027
926
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
927
|
+
const channel: any = await this.assertChannel();
|
|
928
|
+
try {
|
|
929
|
+
await Promise.all([
|
|
930
|
+
channel.waitForConnect(),
|
|
931
|
+
...this.consumers.map((c: AfConsumer) => channel.checkQueue(c.queue)),
|
|
932
|
+
]);
|
|
933
|
+
} catch (e) {
|
|
934
|
+
logger.error('rabbit: isConnected - false');
|
|
935
|
+
return false;
|
|
936
|
+
}
|
|
937
|
+
logger.info('rabbit: isConnected - true');
|
|
938
|
+
return true;
|
|
1031
939
|
}
|
|
1032
940
|
|
|
1033
941
|
async gracefulShutdown(signal: string) : Promise<void> {
|
|
1034
|
-
// TODO:
|
|
1035
|
-
this.
|
|
1036
|
-
const tagsNumber = this.consumersTags.size + this.oldConsumersTags.size;
|
|
942
|
+
// TODO: DELETE OLD
|
|
943
|
+
const tagsNumber = this.consumersTags.length + this.oldConsumersTags.length;
|
|
1037
944
|
logger.info(`rabbit: [gracefully-shutdown] received ${signal}! canceling #${tagsNumber} tags...`);
|
|
1038
|
-
const cancelTagPromises =
|
|
1039
|
-
|
|
1040
|
-
);
|
|
1041
|
-
const cancelTagPromisesOld = [...this.oldConsumersTags].map(
|
|
1042
|
-
([_, { channel, consumerTag }]) => channel.cancel(consumerTag),
|
|
1043
|
-
);
|
|
945
|
+
const cancelTagPromises = this.consumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
946
|
+
const cancelTagPromisesOld = this.oldConsumersTags.map(([channel, tag]) => channel.cancel(tag));
|
|
1044
947
|
// Clean the array to avoid race
|
|
1045
|
-
this.consumersTags
|
|
1046
|
-
this.oldConsumersTags
|
|
948
|
+
this.consumersTags = [];
|
|
949
|
+
this.oldConsumersTags = [];
|
|
1047
950
|
const results = await Promise.allSettled([...cancelTagPromises, ...cancelTagPromisesOld]);
|
|
1048
951
|
const rejected = results.filter((p) => p.status === 'rejected');
|
|
1049
952
|
if (rejected.length > 0) {
|
|
@@ -1056,17 +959,18 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1056
959
|
private async consumeFromRabbitOld(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any> {
|
|
1057
960
|
const optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
|
|
1058
961
|
RabbitMq.validateName('queue', queue);
|
|
962
|
+
this.saveConsumerOld(queue, callback, options);
|
|
1059
963
|
const uniqueId = randomUUID();
|
|
1060
964
|
const {
|
|
1061
965
|
limit, deadMessageTtl, useConsumeWithLock, lockTimeout, auditContext, enableRabbitTrace,
|
|
1062
966
|
} = optionsWithDefaults;
|
|
1063
967
|
if (useConsumeWithLock) {
|
|
1064
968
|
if (!this.redisLock) {
|
|
1065
|
-
throw new
|
|
969
|
+
throw new Error('Usage of consumeWithLock requires RedisInstance');
|
|
1066
970
|
}
|
|
1067
971
|
logger.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
1068
972
|
}
|
|
1069
|
-
const channel = await this.getNewChannelOld({
|
|
973
|
+
const channel = await this.getNewChannelOld({});
|
|
1070
974
|
return channel.addSetup(async (confirmChannel: ConfirmChannel) => {
|
|
1071
975
|
const q = await this.assertQueueOld(queue, optionsWithDefaults);
|
|
1072
976
|
await confirmChannel.prefetch(limit, false);
|
|
@@ -1077,9 +981,9 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1077
981
|
return null;
|
|
1078
982
|
}
|
|
1079
983
|
|
|
1080
|
-
const traceId = msg.properties.headers
|
|
1081
|
-
const userId = msg.properties.headers
|
|
1082
|
-
const automationId = msg.properties.headers
|
|
984
|
+
const traceId = msg.properties.headers[TRACING_HEADER];
|
|
985
|
+
const userId = msg.properties.headers[USER_TRACING_HEADER];
|
|
986
|
+
const automationId = msg.properties.headers[AUTOMATION_ID_HEADER];
|
|
1083
987
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
1084
988
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
1085
989
|
const trace = newTrace(traceTypes.RABBIT);
|
|
@@ -1148,34 +1052,24 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1148
1052
|
}, CONSUMER_DEFAULT_OPTIONS,
|
|
1149
1053
|
);
|
|
1150
1054
|
if (!consumerTag) {
|
|
1151
|
-
logger.error(`rabbit: failed to consume from queue ${queue}
|
|
1055
|
+
logger.error(`rabbit: failed to consume from queue ${queue}`);
|
|
1152
1056
|
} else {
|
|
1153
|
-
logger.info(`rabbit: adding tag ${consumerTag} to the array
|
|
1154
|
-
this.oldConsumersTags.
|
|
1057
|
+
logger.info(`rabbit: adding tag ${consumerTag} to the array.`);
|
|
1058
|
+
this.oldConsumersTags.push([confirmChannel, consumerTag]);
|
|
1155
1059
|
}
|
|
1156
1060
|
});
|
|
1157
1061
|
}
|
|
1158
1062
|
|
|
1159
|
-
// TODO:
|
|
1160
|
-
async getNewChannelOld({
|
|
1161
|
-
|
|
1162
|
-
onClose = null,
|
|
1163
|
-
options = {},
|
|
1164
|
-
connection,
|
|
1165
|
-
}: newChannelOpts) {
|
|
1166
|
-
let localConnection!: AmqpConnectionManager;
|
|
1063
|
+
// TODO: DELETE OLD PROPERTIES UNDER THIS LINE
|
|
1064
|
+
async getNewChannelOld({ name = rand().toString(), onClose = null, options = {} }: newChannelOpts = {}) {
|
|
1065
|
+
let connection!: AmqpConnectionManager;
|
|
1167
1066
|
try {
|
|
1168
|
-
|
|
1067
|
+
connection = await this.getConnectionOld();
|
|
1169
1068
|
} catch (e) {
|
|
1170
1069
|
logger.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
1171
1070
|
throw e;
|
|
1172
1071
|
}
|
|
1173
|
-
|
|
1174
|
-
if (!localConnection) {
|
|
1175
|
-
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
const channel = localConnection?.createChannel({ ...options });
|
|
1072
|
+
const channel = connection.createChannel({ ...options });
|
|
1179
1073
|
once(channel, 'close').then((args) => {
|
|
1180
1074
|
logger.error(`rabbit: channel ${name} closed`);
|
|
1181
1075
|
onClose?.(args);
|
|
@@ -1190,38 +1084,30 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1190
1084
|
}
|
|
1191
1085
|
}
|
|
1192
1086
|
|
|
1193
|
-
async getConnectionOld(
|
|
1087
|
+
async getConnectionOld() {
|
|
1194
1088
|
return new Promise<AmqpConnectionManager>(async (resolve, reject) => {
|
|
1195
|
-
|
|
1196
|
-
amqpConnection: connectionLocal,
|
|
1197
|
-
creatingConnection,
|
|
1198
|
-
connectionCreatedEventName,
|
|
1199
|
-
connectionFailedEventName,
|
|
1200
|
-
blockReconnect,
|
|
1201
|
-
} = connection;
|
|
1202
|
-
|
|
1203
|
-
if (blockReconnect) {
|
|
1089
|
+
if (this.oldBlockReconnect) {
|
|
1204
1090
|
debug('rabbit: block reconnect');
|
|
1205
1091
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1206
1092
|
// @ts-ignore
|
|
1207
1093
|
return resolve();
|
|
1208
1094
|
}
|
|
1209
|
-
if (
|
|
1210
|
-
if (this.options?.disableReconnect ||
|
|
1095
|
+
if (this.oldConnection !== null) {
|
|
1096
|
+
if (this.options?.disableReconnect || this.oldConnection?.isConnected()) {
|
|
1211
1097
|
debug('rabbit: connection - is connected');
|
|
1212
1098
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1213
1099
|
// @ts-ignore
|
|
1214
|
-
return resolve(
|
|
1100
|
+
return resolve(this.oldConnection);
|
|
1215
1101
|
}
|
|
1216
1102
|
debug('rabbit: connection - reconnecting');
|
|
1217
1103
|
}
|
|
1218
|
-
if (
|
|
1104
|
+
if (this.oldCreatingConnection) {
|
|
1219
1105
|
debug('rabbit: creating connection emi');
|
|
1220
|
-
this.oldEm.once(
|
|
1221
|
-
this.oldEm.once(
|
|
1106
|
+
this.oldEm.once(CONNECTION_CREATED_CONST, resolve);
|
|
1107
|
+
this.oldEm.once(CONNECTION_FAILED_CONST, reject);
|
|
1222
1108
|
return;
|
|
1223
1109
|
}
|
|
1224
|
-
|
|
1110
|
+
this.oldCreatingConnection = true;
|
|
1225
1111
|
let isResolved = false;
|
|
1226
1112
|
|
|
1227
1113
|
// It is import to use it as a function and not as a variable
|
|
@@ -1238,59 +1124,56 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1238
1124
|
};
|
|
1239
1125
|
|
|
1240
1126
|
const defaultUrls = findServers();
|
|
1241
|
-
const
|
|
1127
|
+
const connection: AmqpConnectionManager = await connect(defaultUrls, {
|
|
1242
1128
|
findServers,
|
|
1243
1129
|
});
|
|
1244
1130
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1131
|
+
this.oldConnection = connection;
|
|
1132
|
+
this.oldConnection.on('error', (err) => {
|
|
1247
1133
|
logger.error('rabbit: connection error', { err });
|
|
1248
1134
|
if (!isResolved) {
|
|
1249
1135
|
isResolved = true;
|
|
1250
1136
|
reject(err);
|
|
1251
|
-
this.oldEm.emit(
|
|
1137
|
+
this.oldEm.emit(CONNECTION_FAILED_CONST, err);
|
|
1252
1138
|
}
|
|
1253
1139
|
});
|
|
1254
1140
|
|
|
1255
|
-
|
|
1256
|
-
this.oldConsumersTags
|
|
1257
|
-
if (typeof err.url === 'string') {
|
|
1258
|
-
err.url = this.maskURL(err.url);
|
|
1259
|
-
}
|
|
1141
|
+
this.oldConnection.on('connectFailed', (err) => {
|
|
1142
|
+
this.oldConsumersTags = [];
|
|
1260
1143
|
logger.error('rabbit: connection connectFailed', { err });
|
|
1261
1144
|
if (!isResolved) {
|
|
1262
1145
|
isResolved = true;
|
|
1263
1146
|
reject(err);
|
|
1264
|
-
this.oldEm.emit(
|
|
1147
|
+
this.oldEm.emit(CONNECTION_FAILED_CONST, err);
|
|
1265
1148
|
}
|
|
1266
1149
|
});
|
|
1267
1150
|
|
|
1268
|
-
|
|
1269
|
-
this.oldConsumersTags
|
|
1151
|
+
this.oldConnection.on('disconnect', ({ err }) => {
|
|
1152
|
+
this.oldConsumersTags = [];
|
|
1270
1153
|
debug('rabbit: connection closed');
|
|
1271
1154
|
if (this.options?.disableReconnect) {
|
|
1272
1155
|
logger.error(`${this.DISCONNECT_MSG}${err && ` - ${err}`}`);
|
|
1273
|
-
|
|
1156
|
+
this.oldBlockReconnect = true;
|
|
1274
1157
|
} else {
|
|
1275
1158
|
logger.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
1276
1159
|
}
|
|
1277
1160
|
});
|
|
1278
1161
|
|
|
1279
|
-
|
|
1162
|
+
this.oldConnection.once('connect', async () => {
|
|
1280
1163
|
debug('rabbit: connection established');
|
|
1281
|
-
|
|
1282
|
-
this.oldEm.emit(
|
|
1164
|
+
this.oldCreatingConnection = false;
|
|
1165
|
+
this.oldEm.emit(CONNECTION_CREATED_CONST, connection);
|
|
1283
1166
|
isResolved = true;
|
|
1284
|
-
resolve(
|
|
1167
|
+
resolve(connection);
|
|
1285
1168
|
});
|
|
1286
1169
|
});
|
|
1287
1170
|
}
|
|
1288
1171
|
|
|
1289
1172
|
private saveConsumerOld(queue: string, callback: CallbackFunction, options: ConsumeOptions | undefined) {
|
|
1290
|
-
const isConsumerExist:
|
|
1173
|
+
const isConsumerExist :boolean = this.oldConsumers.some((consumer) => consumer.queue === queue);
|
|
1291
1174
|
if (!isConsumerExist) {
|
|
1292
1175
|
logger.info(`rabbit: consumer: ${queue} saved in consumer array`);
|
|
1293
|
-
this.
|
|
1176
|
+
this.oldConsumers.push({
|
|
1294
1177
|
queue,
|
|
1295
1178
|
callback,
|
|
1296
1179
|
options,
|
|
@@ -1315,7 +1198,6 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1315
1198
|
|
|
1316
1199
|
async setupQueueOld(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue> {
|
|
1317
1200
|
let queue: Replies.AssertQueue;
|
|
1318
|
-
const connection = this.oldPublishConnection;
|
|
1319
1201
|
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
1320
1202
|
const localeOptions = {
|
|
1321
1203
|
...options,
|
|
@@ -1327,7 +1209,7 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1327
1209
|
},
|
|
1328
1210
|
};
|
|
1329
1211
|
try {
|
|
1330
|
-
const channel: ChannelWrapper = await this.assertChannelOld(
|
|
1212
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1331
1213
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
1332
1214
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
1333
1215
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
@@ -1336,8 +1218,8 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1336
1218
|
logger.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
1337
1219
|
if (!this.options?.dontRetryAssert) {
|
|
1338
1220
|
debug('retrying assertQueue', { queueName });
|
|
1339
|
-
const channel = await this.assertChannelOld({ force: true
|
|
1340
|
-
await this.deleteQueueOld(queueName
|
|
1221
|
+
const channel = await this.assertChannelOld({ force: true });
|
|
1222
|
+
await this.deleteQueueOld(queueName);
|
|
1341
1223
|
|
|
1342
1224
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
1343
1225
|
await channel.addSetup((setupChannel: ConfirmChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
@@ -1352,21 +1234,19 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1352
1234
|
return queue;
|
|
1353
1235
|
}
|
|
1354
1236
|
|
|
1355
|
-
async assertChannelOld({ force = false
|
|
1237
|
+
async assertChannelOld({ force = false } : assertChannelOpts = {}): Promise<ChannelWrapper> {
|
|
1356
1238
|
if (!this.oldPublishChannelSetupPromise) {
|
|
1357
1239
|
this.oldPublishChannelSetupPromise = new Promise<ChannelWrapper>(async (resolve, reject) => {
|
|
1358
|
-
if (this.
|
|
1359
|
-
return resolve(this.
|
|
1240
|
+
if (this.oldChannel && !force) {
|
|
1241
|
+
return resolve(this.oldChannel);
|
|
1360
1242
|
}
|
|
1361
1243
|
|
|
1362
1244
|
try {
|
|
1363
|
-
const channel = await this.getNewChannelOld({
|
|
1245
|
+
const channel = await this.getNewChannelOld({});
|
|
1364
1246
|
channel.on('error', (err) => {
|
|
1365
1247
|
logger.error('rabbit: channel error', { err });
|
|
1366
1248
|
});
|
|
1367
|
-
|
|
1368
|
-
this.oldPublishChannel = channel;
|
|
1369
|
-
}
|
|
1249
|
+
this.oldChannel = channel;
|
|
1370
1250
|
resolve(channel);
|
|
1371
1251
|
} catch (e) {
|
|
1372
1252
|
reject(e);
|
|
@@ -1376,17 +1256,17 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1376
1256
|
return this.oldPublishChannelSetupPromise;
|
|
1377
1257
|
}
|
|
1378
1258
|
|
|
1379
|
-
private async deleteQueueOld(queue: string
|
|
1259
|
+
private async deleteQueueOld(queue: string) {
|
|
1380
1260
|
RabbitMq.validateName('queue', queue);
|
|
1381
|
-
const channel: ChannelWrapper = await this.assertChannelOld(
|
|
1261
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1382
1262
|
logger.info('rabbit: deleting queue', { queue });
|
|
1383
1263
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
1384
1264
|
debug('queue deleted', deleteQueueRes);
|
|
1385
1265
|
return deleteQueueRes;
|
|
1386
1266
|
}
|
|
1387
1267
|
|
|
1388
|
-
async assertExchangeOld(exchangeName: string,
|
|
1389
|
-
const channel: ChannelWrapper = await this.assertChannelOld(
|
|
1268
|
+
async assertExchangeOld(exchangeName: string, options?: any) {
|
|
1269
|
+
const channel: ChannelWrapper = await this.assertChannelOld();
|
|
1390
1270
|
|
|
1391
1271
|
if (this.oldExchanges[exchangeName]) {
|
|
1392
1272
|
delete this.oldAssertExchangePromises[exchangeName];
|
|
@@ -1401,60 +1281,6 @@ class RabbitMq implements IAfRabbitMq {
|
|
|
1401
1281
|
this.oldExchanges[exchangeName] = await this.oldAssertExchangePromises[exchangeName];
|
|
1402
1282
|
return this.oldExchanges[exchangeName];
|
|
1403
1283
|
}
|
|
1404
|
-
|
|
1405
|
-
async isConnectedOld() : Promise<boolean> {
|
|
1406
|
-
if (!this.gracefulShutdownStarted) {
|
|
1407
|
-
debug('rabbit: start is connected old');
|
|
1408
|
-
const [oldConsumeConnection, oldPublishConnection] = await Promise.all([
|
|
1409
|
-
this.getConnectionOld(this.oldConsumeConnection),
|
|
1410
|
-
this.getConnectionOld(this.oldPublishConnection),
|
|
1411
|
-
]);
|
|
1412
|
-
const isConnected = oldConsumeConnection.isConnected() && oldPublishConnection.isConnected();
|
|
1413
|
-
if (!isConnected) {
|
|
1414
|
-
logger.error('rabbit: isConnected old - false');
|
|
1415
|
-
return false;
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
try {
|
|
1419
|
-
const unRegisteredConsumersOld = this.consumersToRegister.filter((c: AfConsumer) => {
|
|
1420
|
-
const doesConsumerExist = this.consumersTags.get(c.queue);
|
|
1421
|
-
return !doesConsumerExist;
|
|
1422
|
-
});
|
|
1423
|
-
|
|
1424
|
-
if (unRegisteredConsumersOld.length > 0) {
|
|
1425
|
-
const queueNames = unRegisteredConsumersOld.map((c) => c.queue);
|
|
1426
|
-
|
|
1427
|
-
logger.error('rabbit: found unregistered consumers for queues old', {
|
|
1428
|
-
count: queueNames.length,
|
|
1429
|
-
queues: queueNames,
|
|
1430
|
-
});
|
|
1431
|
-
throw new RabbitError('Found unregistered consumers old');
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
|
-
const channelOld: any = await this.assertChannelOld({ connection: this.oldPublishConnection });
|
|
1435
|
-
await channelOld.waitForConnect();
|
|
1436
|
-
|
|
1437
|
-
await Promise.all(
|
|
1438
|
-
this.oldConsumersToRegister.map((c: AfConsumer) => channelOld.checkQueue(c.queue)),
|
|
1439
|
-
);
|
|
1440
|
-
} catch (e: RabbitError | any) {
|
|
1441
|
-
logger.error('rabbit: isConnected - false old', { msg: e.message });
|
|
1442
|
-
return false;
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
return true;
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
private maskURL = (url: string): string => {
|
|
1449
|
-
try {
|
|
1450
|
-
const urlObj = new URL(url);
|
|
1451
|
-
urlObj.username = '***';
|
|
1452
|
-
urlObj.password = '***';
|
|
1453
|
-
return urlObj.toString();
|
|
1454
|
-
} catch {
|
|
1455
|
-
return url;
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
1284
|
}
|
|
1459
1285
|
|
|
1460
1286
|
export default RabbitMq;
|