@autofleet/rabbit 3.3.0-beta.0 → 3.3.0-beta.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/index.d.ts +18 -12
- package/dist/index.js +185 -107
- package/dist/lib/celery.d.ts +9 -0
- package/dist/lib/celery.js +54 -0
- package/dist/lib/consts.d.ts +5 -2
- package/dist/lib/consts.js +7 -3
- package/dist/lib/types.d.ts +10 -0
- package/dist/lib/utils.d.ts +2 -2
- package/package.json +5 -2
- package/src/index.ts +230 -132
- package/src/lib/celery.ts +89 -0
- package/src/lib/consts.ts +6 -2
- package/src/lib/types.ts +12 -0
- package/src/lib/utils.ts +6 -2
- package/coverage/clover.xml +0 -7
- package/coverage/coverage-final.json +0 -1
- package/coverage/lcov-report/base.css +0 -212
- package/coverage/lcov-report/index.html +0 -60
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -1
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -158
- package/coverage/lcov.info +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import { EventEmitter } from 'events';
|
|
|
3
3
|
import { AmqpConnectionManager, ChannelWrapper, CreateChannelOpts } from 'amqp-connection-manager';
|
|
4
4
|
import { ConfirmChannel, ConsumeMessage, Options, Replies } from 'amqplib';
|
|
5
5
|
import { RedisConfig } from './lib/redis';
|
|
6
|
-
import {
|
|
6
|
+
import { ConnectionPurpose } from './lib/consts';
|
|
7
|
+
import { CallbackFunction, ConsumeMessageOrNull, ConsumeOptions, CustomMessageHeaders, QueuesCache, RedisLockType, ExchangesCache, QueueSetupPromisesDictionary, AssertExchangePromisesDictionary, ConnectionData } from './lib/types';
|
|
7
8
|
export interface IAfRabbitMq {
|
|
8
9
|
ack: any;
|
|
9
10
|
nack: any;
|
|
@@ -32,17 +33,17 @@ export interface AfRabbitOptions {
|
|
|
32
33
|
*/
|
|
33
34
|
dontRetryAssert?: boolean;
|
|
34
35
|
rabbitHost?: string;
|
|
35
|
-
serviceName: string;
|
|
36
|
-
podIp?: string;
|
|
37
36
|
}
|
|
38
37
|
type newChannelOpts = {
|
|
39
38
|
name?: string;
|
|
40
39
|
onClose?: null | ((args: any | null) => void);
|
|
41
40
|
options?: CreateChannelOpts | undefined;
|
|
41
|
+
connectionPurpose?: ConnectionPurpose;
|
|
42
42
|
};
|
|
43
43
|
type assertChannelOpts = {
|
|
44
44
|
channelName?: string;
|
|
45
45
|
force?: boolean;
|
|
46
|
+
connectionPurpose?: ConnectionPurpose;
|
|
46
47
|
};
|
|
47
48
|
declare class RabbitMq implements IAfRabbitMq {
|
|
48
49
|
static parseMsg(msg: any): any;
|
|
@@ -59,15 +60,18 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
59
60
|
};
|
|
60
61
|
DISCONNECT_MSG: string;
|
|
61
62
|
RECONNECT_MSG: string;
|
|
62
|
-
|
|
63
|
+
publishChannel: ChannelWrapper | null;
|
|
64
|
+
publishChannelSetupPromise: Promise<ChannelWrapper> | null;
|
|
63
65
|
blockReconnect: boolean | null | undefined;
|
|
64
|
-
|
|
66
|
+
connectionsMap: {
|
|
67
|
+
[ConnectionPurpose.Consume]: ConnectionData;
|
|
68
|
+
[ConnectionPurpose.Publish]: ConnectionData;
|
|
69
|
+
};
|
|
65
70
|
em: EventEmitter;
|
|
66
|
-
podId: string;
|
|
67
|
-
creatingConnection: boolean;
|
|
68
71
|
exchanges: ExchangesCache;
|
|
69
72
|
queues: QueuesCache;
|
|
70
73
|
queueSetupPromises: QueueSetupPromisesDictionary;
|
|
74
|
+
assertExchangePromises: AssertExchangePromisesDictionary;
|
|
71
75
|
options: AfRabbitOptions | undefined;
|
|
72
76
|
redisClient: any;
|
|
73
77
|
redisLock?: RedisLockType;
|
|
@@ -78,14 +82,15 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
78
82
|
private shouldConsumeMessageByTimestamp;
|
|
79
83
|
ack: (channel: ConfirmChannel, msg: ConsumeMessageOrNull, shouldUpdateRedisTimestamp?: boolean, releaseLock?: null) => (userMsg: ConsumeMessage) => Promise<any>;
|
|
80
84
|
nack: (channel: ConfirmChannel, queue: string, options: any, deadQueueOptions: Options.AssertQueue, msg: ConsumeMessageOrNull, releaseLock: any) => (userMsg: ConsumeMessageOrNull, { skipRetry, }?: NackOptions) => Promise<any>;
|
|
81
|
-
getConnection(): Promise<AmqpConnectionManager>;
|
|
82
|
-
getNewChannel({ name, onClose, options }
|
|
83
|
-
assertChannel({ force }
|
|
85
|
+
getConnection(connectionPurpose: ConnectionPurpose): Promise<AmqpConnectionManager | null | undefined>;
|
|
86
|
+
getNewChannel({ name, onClose, options, connectionPurpose, }: newChannelOpts): Promise<ChannelWrapper>;
|
|
87
|
+
assertChannel({ force, connectionPurpose }: assertChannelOpts): Promise<ChannelWrapper>;
|
|
84
88
|
assertExchange(exchangeName: string, options?: any): Promise<any>;
|
|
85
|
-
getQueueLength(queue: string): Promise<Replies.AssertQueue>;
|
|
89
|
+
getQueueLength(queue: string, connectionPurpose?: ConnectionPurpose): Promise<Replies.AssertQueue>;
|
|
86
90
|
private deleteQueue;
|
|
87
91
|
bindQueue(queue: string, exchange: string): Promise<void>;
|
|
88
92
|
setupQueue(queueName: string, options?: Options.AssertQueue): Promise<Replies.AssertQueue>;
|
|
93
|
+
static shouldUseQuorum(queueName: string): boolean;
|
|
89
94
|
assertQueue(queueName: string, options?: Options.AssertQueue): Promise<any>;
|
|
90
95
|
private saveConsumer;
|
|
91
96
|
consume(queue: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
@@ -94,8 +99,9 @@ declare class RabbitMq implements IAfRabbitMq {
|
|
|
94
99
|
private consumeFromRabbit;
|
|
95
100
|
consumeFromExchange(queue: string, exchange: string, callback: CallbackFunction, options?: ConsumeOptions): Promise<any>;
|
|
96
101
|
publish(exchange: string, content: any, customHeaders?: any): Promise<boolean>;
|
|
97
|
-
sendToQueue(queue: string, content: any, options?: any, customHeaders?: any
|
|
102
|
+
sendToQueue(queue: string, content: any, options?: any, customHeaders?: any): Promise<boolean | undefined>;
|
|
98
103
|
isConnected(): Promise<boolean>;
|
|
99
104
|
gracefulShutdown(signal: string): Promise<void>;
|
|
100
105
|
}
|
|
101
106
|
export default RabbitMq;
|
|
107
|
+
export { sendCeleryTaskViaHttp, } from './lib/celery';
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.sendCeleryTaskViaHttp = void 0;
|
|
7
8
|
const events_1 = require("events");
|
|
8
9
|
const util_1 = require("util");
|
|
9
10
|
const moment_1 = __importDefault(require("moment"));
|
|
@@ -116,15 +117,28 @@ class RabbitMq {
|
|
|
116
117
|
}
|
|
117
118
|
};
|
|
118
119
|
this.em = new events_1.EventEmitter();
|
|
119
|
-
this.
|
|
120
|
-
this.
|
|
121
|
-
this.
|
|
120
|
+
this.publishChannel = null;
|
|
121
|
+
this.publishChannelSetupPromise = null;
|
|
122
|
+
this.connectionsMap = {
|
|
123
|
+
[consts_1.ConnectionPurpose.Consume]: {
|
|
124
|
+
connection: null,
|
|
125
|
+
creatingConnection: false,
|
|
126
|
+
connectionCreatedEventName: 'consumeConnectionCreated',
|
|
127
|
+
connectionFailedEventName: 'consumeConnectionFailed',
|
|
128
|
+
},
|
|
129
|
+
[consts_1.ConnectionPurpose.Publish]: {
|
|
130
|
+
connection: null,
|
|
131
|
+
creatingConnection: false,
|
|
132
|
+
connectionCreatedEventName: 'publishConnectionCreated',
|
|
133
|
+
connectionFailedEventName: 'publishConnectionFailed',
|
|
134
|
+
},
|
|
135
|
+
};
|
|
122
136
|
this.exchanges = {};
|
|
123
137
|
this.queues = {};
|
|
124
138
|
this.queueSetupPromises = {};
|
|
139
|
+
this.assertExchangePromises = {};
|
|
125
140
|
this.consumers = [];
|
|
126
141
|
this.options = options;
|
|
127
|
-
this.podId = `${options?.serviceName}${options?.podIp ? `-${options.podIp}` : ''}`;
|
|
128
142
|
this.redisClient = redisConfig && (0, redis_1.default)(redisConfig);
|
|
129
143
|
if (this.redisClient) {
|
|
130
144
|
this.redisLock = (0, util_1.promisify)((0, redis_lock_1.default)(this.redisClient));
|
|
@@ -140,30 +154,31 @@ class RabbitMq {
|
|
|
140
154
|
});
|
|
141
155
|
}
|
|
142
156
|
}
|
|
143
|
-
async getConnection() {
|
|
157
|
+
async getConnection(connectionPurpose) {
|
|
144
158
|
return new Promise(async (resolve, reject) => {
|
|
159
|
+
const { connection, creatingConnection, connectionCreatedEventName, connectionFailedEventName, } = this.connectionsMap[connectionPurpose];
|
|
145
160
|
if (this.blockReconnect) {
|
|
146
161
|
debug('rabbit: block reconnect');
|
|
147
162
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
148
163
|
// @ts-ignore
|
|
149
164
|
return resolve();
|
|
150
165
|
}
|
|
151
|
-
if (
|
|
152
|
-
if (this.options?.disableReconnect ||
|
|
166
|
+
if (connection !== null) {
|
|
167
|
+
if (this.options?.disableReconnect || connection?.isConnected()) {
|
|
153
168
|
debug('rabbit: connection - is connected');
|
|
154
169
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
155
170
|
// @ts-ignore
|
|
156
|
-
return resolve(
|
|
171
|
+
return resolve(connection);
|
|
157
172
|
}
|
|
158
173
|
debug('rabbit: connection - reconnecting');
|
|
159
174
|
}
|
|
160
|
-
if (
|
|
175
|
+
if (creatingConnection) {
|
|
161
176
|
debug('rabbit: creating connection emi');
|
|
162
|
-
this.em.once(
|
|
163
|
-
this.em.once(
|
|
177
|
+
this.em.once(connectionCreatedEventName, resolve);
|
|
178
|
+
this.em.once(connectionFailedEventName, reject);
|
|
164
179
|
return;
|
|
165
180
|
}
|
|
166
|
-
this.creatingConnection = true;
|
|
181
|
+
this.connectionsMap[connectionPurpose].creatingConnection = true;
|
|
167
182
|
let isResolved = false;
|
|
168
183
|
// It is import to use it as a function and not as a variable
|
|
169
184
|
// because of k8s changes the env variables
|
|
@@ -176,28 +191,29 @@ class RabbitMq {
|
|
|
176
191
|
return [`amqp://${userName}:${password}@${host}?heartbeat=${HEARTBEAT}`];
|
|
177
192
|
};
|
|
178
193
|
const defaultUrls = findServers();
|
|
179
|
-
const
|
|
194
|
+
const newConnection = await (0, amqp_connection_manager_1.connect)(defaultUrls, {
|
|
180
195
|
findServers,
|
|
181
196
|
});
|
|
182
|
-
this.connection =
|
|
183
|
-
|
|
197
|
+
this.connectionsMap[connectionPurpose].connection = newConnection;
|
|
198
|
+
logger_1.default.info(`rabbit: created new connection ${connectionPurpose}`);
|
|
199
|
+
newConnection.on('error', (err) => {
|
|
184
200
|
logger_1.default.error('rabbit: connection error', { err });
|
|
185
201
|
if (!isResolved) {
|
|
186
202
|
isResolved = true;
|
|
187
203
|
reject(err);
|
|
188
|
-
this.em.emit(
|
|
204
|
+
this.em.emit(connectionFailedEventName, err);
|
|
189
205
|
}
|
|
190
206
|
});
|
|
191
|
-
|
|
207
|
+
newConnection.on('connectFailed', (err) => {
|
|
192
208
|
this.consumersTags = [];
|
|
193
209
|
logger_1.default.error('rabbit: connection connectFailed', { err });
|
|
194
210
|
if (!isResolved) {
|
|
195
211
|
isResolved = true;
|
|
196
212
|
reject(err);
|
|
197
|
-
this.em.emit(
|
|
213
|
+
this.em.emit(connectionFailedEventName, err);
|
|
198
214
|
}
|
|
199
215
|
});
|
|
200
|
-
|
|
216
|
+
newConnection.on('disconnect', ({ err }) => {
|
|
201
217
|
this.consumersTags = [];
|
|
202
218
|
debug('rabbit: connection closed');
|
|
203
219
|
if (this.options?.disableReconnect) {
|
|
@@ -208,25 +224,27 @@ class RabbitMq {
|
|
|
208
224
|
logger_1.default.error(`${this.RECONNECT_MSG}${err && ` - ${err}`}`);
|
|
209
225
|
}
|
|
210
226
|
});
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
this.
|
|
214
|
-
this.em.emit(consts_1.CONNECTION_CREATED_CONST, connection);
|
|
227
|
+
newConnection.once('connect', async () => {
|
|
228
|
+
this.connectionsMap[connectionPurpose].creatingConnection = false;
|
|
229
|
+
this.em.emit(connectionCreatedEventName, newConnection);
|
|
215
230
|
isResolved = true;
|
|
216
|
-
resolve(
|
|
231
|
+
resolve(newConnection);
|
|
217
232
|
});
|
|
218
233
|
});
|
|
219
234
|
}
|
|
220
|
-
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}
|
|
235
|
+
async getNewChannel({ name = (0, utils_1.rand)().toString(), onClose = null, options = {}, connectionPurpose = consts_1.ConnectionPurpose.Consume, }) {
|
|
221
236
|
let connection;
|
|
222
237
|
try {
|
|
223
|
-
connection = await this.getConnection();
|
|
238
|
+
connection = await this.getConnection(connectionPurpose);
|
|
224
239
|
}
|
|
225
240
|
catch (e) {
|
|
226
241
|
logger_1.default.error(`rabbit: error on get connection for new channel ${name} `, { e });
|
|
227
242
|
throw e;
|
|
228
243
|
}
|
|
229
|
-
|
|
244
|
+
if (!connection) {
|
|
245
|
+
throw new Error(`rabbit: couldnt get connection for new channel ${name}`);
|
|
246
|
+
}
|
|
247
|
+
const channel = connection.createChannel({ ...options });
|
|
230
248
|
(0, events_1.once)(channel, 'close').then((args) => {
|
|
231
249
|
logger_1.default.error(`rabbit: channel ${name} closed`);
|
|
232
250
|
onClose?.(args);
|
|
@@ -241,83 +259,118 @@ class RabbitMq {
|
|
|
241
259
|
throw err;
|
|
242
260
|
}
|
|
243
261
|
}
|
|
244
|
-
async assertChannel({ force = false
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
262
|
+
async assertChannel({ force = false, connectionPurpose = consts_1.ConnectionPurpose.Consume }) {
|
|
263
|
+
debug('rabbit: start assert channel', { connectionPurpose, publishPromise: this.publishChannelSetupPromise, channel: this.publishChannel });
|
|
264
|
+
if (!this.publishChannelSetupPromise) {
|
|
265
|
+
this.publishChannelSetupPromise = new Promise(async (resolve, reject) => {
|
|
266
|
+
if (this.publishChannel && !force) {
|
|
267
|
+
return resolve(this.publishChannel);
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
const channel = await this.getNewChannel({ connectionPurpose });
|
|
271
|
+
debug('rabbit: new channel got', { connectionPurpose, channel: this.publishChannel });
|
|
272
|
+
channel.on('error', (err) => {
|
|
273
|
+
logger_1.default.error('rabbit: channel error', { err });
|
|
274
|
+
});
|
|
275
|
+
if (connectionPurpose === consts_1.ConnectionPurpose.Publish) {
|
|
276
|
+
this.publishChannel = channel;
|
|
277
|
+
}
|
|
278
|
+
resolve(channel);
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
reject(e);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
return this.publishChannelSetupPromise;
|
|
261
286
|
}
|
|
262
|
-
async assertExchange(exchangeName, options) {
|
|
263
|
-
const channel = await this.assertChannel();
|
|
287
|
+
async assertExchange(exchangeName, options = { connectionPurpose: consts_1.ConnectionPurpose.Consume }) {
|
|
288
|
+
const channel = await this.assertChannel({ connectionPurpose: options.connectionPurpose });
|
|
264
289
|
if (this.exchanges[exchangeName]) {
|
|
290
|
+
delete this.assertExchangePromises[exchangeName];
|
|
265
291
|
return this.exchanges[exchangeName];
|
|
266
292
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
293
|
+
if (this.assertExchangePromises[exchangeName]) {
|
|
294
|
+
return this.assertExchangePromises[exchangeName];
|
|
295
|
+
}
|
|
296
|
+
this.assertExchangePromises[exchangeName] = (0, utils_1.assertExchangeFanout)(channel, exchangeName);
|
|
297
|
+
this.exchanges[exchangeName] = await this.assertExchangePromises[exchangeName];
|
|
298
|
+
return this.exchanges[exchangeName];
|
|
270
299
|
}
|
|
271
|
-
async getQueueLength(queue) {
|
|
300
|
+
async getQueueLength(queue, connectionPurpose = consts_1.ConnectionPurpose.Consume) {
|
|
272
301
|
RabbitMq.validateName('queue', queue);
|
|
273
|
-
const {
|
|
274
|
-
|
|
302
|
+
const { connection } = this.connectionsMap[connectionPurpose];
|
|
303
|
+
const { publishChannel } = this;
|
|
304
|
+
if (!publishChannel) {
|
|
275
305
|
throw new Error('channel is not defined');
|
|
276
306
|
}
|
|
277
|
-
debug('rabbit: getting queue length', { queue, connected:
|
|
278
|
-
return
|
|
307
|
+
debug('rabbit: getting queue length', { queue, connected: connection?.isConnected() });
|
|
308
|
+
return publishChannel?.checkQueue(queue);
|
|
279
309
|
}
|
|
280
|
-
async deleteQueue(queue) {
|
|
310
|
+
async deleteQueue(queue, connectionPurpose) {
|
|
281
311
|
RabbitMq.validateName('queue', queue);
|
|
282
|
-
const channel = await this.assertChannel();
|
|
312
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
283
313
|
logger_1.default.info('rabbit: deleting queue', { queue });
|
|
284
314
|
const deleteQueueRes = await channel.deleteQueue(queue);
|
|
285
315
|
debug('queue deleted', deleteQueueRes);
|
|
286
316
|
return deleteQueueRes;
|
|
287
317
|
}
|
|
288
318
|
async bindQueue(queue, exchange) {
|
|
289
|
-
const channel = await this.assertChannel();
|
|
319
|
+
const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
290
320
|
await channel.addSetup((setupChannel) => setupChannel.bindQueue(queue, exchange, ''));
|
|
291
321
|
return channel.bindQueue(queue, exchange, '');
|
|
292
322
|
}
|
|
293
323
|
async setupQueue(queueName, options) {
|
|
294
324
|
let queue;
|
|
325
|
+
const connectionPurpose = consts_1.ConnectionPurpose.Publish;
|
|
326
|
+
const shouldUseQuorum = RabbitMq.shouldUseQuorum(queueName);
|
|
327
|
+
const localeOptions = {
|
|
328
|
+
...options,
|
|
329
|
+
durable: true,
|
|
330
|
+
arguments: {
|
|
331
|
+
...options?.arguments,
|
|
332
|
+
'x-consumer-timeout': 1000 * 60 * 60 * 24,
|
|
333
|
+
'x-queue-type': shouldUseQuorum ? 'quorum' : 'classic',
|
|
334
|
+
},
|
|
335
|
+
};
|
|
295
336
|
try {
|
|
296
|
-
const channel = await this.assertChannel();
|
|
337
|
+
const channel = await this.assertChannel({ connectionPurpose });
|
|
297
338
|
debug('assertQueue->channel.addSetup', { queueName });
|
|
298
|
-
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName,
|
|
339
|
+
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
299
340
|
debug('assertQueue->channel.assertQueue', { queueName });
|
|
300
|
-
queue = await channel.assertQueue(queueName,
|
|
341
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
301
342
|
}
|
|
302
343
|
catch (e) {
|
|
303
344
|
logger_1.default.error('rabbit: assertQueue error', { queueName, options, error: e });
|
|
304
345
|
if (!this.options?.dontRetryAssert) {
|
|
305
346
|
debug('retrying assertQueue', { queueName });
|
|
306
|
-
const channel = await this.assertChannel({ force: true });
|
|
307
|
-
await this.deleteQueue(queueName);
|
|
347
|
+
const channel = await this.assertChannel({ force: true, connectionPurpose });
|
|
348
|
+
await this.deleteQueue(queueName, connectionPurpose);
|
|
308
349
|
debug('retrying assertQueue->channel.addSetup', { queueName });
|
|
309
|
-
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName,
|
|
350
|
+
await channel.addSetup((setupChannel) => setupChannel.assertQueue(queueName, localeOptions));
|
|
310
351
|
debug('retrying assertQueue->channel.assertQueue', { queueName });
|
|
311
|
-
queue = await channel.assertQueue(queueName,
|
|
352
|
+
queue = await channel.assertQueue(queueName, localeOptions);
|
|
312
353
|
}
|
|
313
354
|
else {
|
|
314
355
|
throw e;
|
|
315
356
|
}
|
|
316
357
|
}
|
|
317
|
-
this.queues[queueName] =
|
|
358
|
+
this.queues[queueName] = queue;
|
|
318
359
|
return queue;
|
|
319
360
|
}
|
|
361
|
+
static shouldUseQuorum(queueName) {
|
|
362
|
+
const envQuorumQueuesWhitelist = process.env.QUORUM_QUEUES_WHITELIST;
|
|
363
|
+
if (envQuorumQueuesWhitelist === '*') {
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
if (envQuorumQueuesWhitelist) {
|
|
367
|
+
const whitelist = envQuorumQueuesWhitelist.split(',');
|
|
368
|
+
return whitelist.includes(queueName);
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
320
372
|
async assertQueue(queueName, options) {
|
|
373
|
+
debug('rabbit: start assert queue', { queueName });
|
|
321
374
|
RabbitMq.validateName('queue', queueName);
|
|
322
375
|
if (this.queues[queueName]) {
|
|
323
376
|
delete this.queueSetupPromises[queueName];
|
|
@@ -327,6 +380,7 @@ class RabbitMq {
|
|
|
327
380
|
return this.queueSetupPromises[queueName];
|
|
328
381
|
}
|
|
329
382
|
this.queueSetupPromises[queueName] = this.setupQueue(queueName, options);
|
|
383
|
+
debug('rabbit: done assert queue', { queueName });
|
|
330
384
|
return this.queueSetupPromises[queueName];
|
|
331
385
|
}
|
|
332
386
|
saveConsumer(queue, callback, options) {
|
|
@@ -369,16 +423,17 @@ class RabbitMq {
|
|
|
369
423
|
}
|
|
370
424
|
logger_1.default.info(`rabbit: Consuming with lock from queue ${queue} with lockTimeout: ${lockTimeout}ms`);
|
|
371
425
|
}
|
|
372
|
-
const channel = await this.getNewChannel({
|
|
426
|
+
const channel = await this.getNewChannel({ connectionPurpose: consts_1.ConnectionPurpose.Consume });
|
|
373
427
|
return channel.addSetup(async (confirmChannel) => {
|
|
374
|
-
await
|
|
375
|
-
await confirmChannel.prefetch(limit,
|
|
428
|
+
const q = await this.assertQueue(queue, optionsWithDefaults);
|
|
429
|
+
await confirmChannel.prefetch(limit, false);
|
|
376
430
|
const { consumerTag } = await confirmChannel.consume(queue, async (msg) => {
|
|
377
431
|
if (!msg) {
|
|
378
432
|
return null;
|
|
379
433
|
}
|
|
380
434
|
const traceId = msg.properties.headers[consts_1.TRACING_HEADER];
|
|
381
435
|
const userId = msg.properties.headers[consts_1.USER_TRACING_HEADER];
|
|
436
|
+
const automationId = msg.properties.headers[consts_1.AUTOMATION_ID_HEADER];
|
|
382
437
|
const parsedMessage = RabbitMq.parseMsg(msg);
|
|
383
438
|
const releaseLock = await this.lockRedisIfNeeded(parsedMessage, optionsWithDefaults);
|
|
384
439
|
const trace = (0, zehut_1.newTrace)(zehut_1.traceTypes.RABBIT);
|
|
@@ -403,7 +458,10 @@ class RabbitMq {
|
|
|
403
458
|
outbreakTrace?.context.set(consts_1.TRACING_HEADER, traceId);
|
|
404
459
|
}
|
|
405
460
|
if (auditContext) {
|
|
406
|
-
await auditContext(queue
|
|
461
|
+
await auditContext(queue, {
|
|
462
|
+
userId,
|
|
463
|
+
automationId,
|
|
464
|
+
});
|
|
407
465
|
}
|
|
408
466
|
const shouldConsume = await this.shouldConsumeMessageByTimestamp(parsedMessage);
|
|
409
467
|
if (!shouldConsume) {
|
|
@@ -449,12 +507,12 @@ class RabbitMq {
|
|
|
449
507
|
RabbitMq.validateName('queue', queue);
|
|
450
508
|
const { limit, deadMessageTtl } = optionsWithDefaults;
|
|
451
509
|
await this.saveConsumer(queue, callback, options);
|
|
452
|
-
const channel = await this.getNewChannel({ name:
|
|
510
|
+
const channel = await this.getNewChannel({ name: `consume - exchange - ${exchange} -queue - ${queue}` });
|
|
453
511
|
return channel.addSetup(async (c) => {
|
|
454
512
|
const assertExchange = await (0, utils_1.assertExchangeFanout)(c, exchange);
|
|
455
513
|
await c.assertQueue(queue);
|
|
456
514
|
this.exchanges[exchange] = assertExchange;
|
|
457
|
-
await c.prefetch(limit,
|
|
515
|
+
await c.prefetch(limit, false);
|
|
458
516
|
return Promise.all([
|
|
459
517
|
c.bindQueue(queue, exchange, ''),
|
|
460
518
|
this.consume(queue, callback, options),
|
|
@@ -462,53 +520,71 @@ class RabbitMq {
|
|
|
462
520
|
});
|
|
463
521
|
}
|
|
464
522
|
async publish(exchange, content, customHeaders) {
|
|
523
|
+
debug('rabbit: start publish msg');
|
|
465
524
|
return (0, utils_1.wrapSetImmediate)(async () => {
|
|
466
525
|
RabbitMq.validateName('exchange', exchange);
|
|
467
|
-
const channel = await this.assertChannel();
|
|
468
|
-
await this.assertExchange(exchange);
|
|
526
|
+
const channel = await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
527
|
+
await this.assertExchange(exchange, { connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
469
528
|
await channel.publish(exchange, '', Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
470
529
|
});
|
|
471
530
|
}
|
|
472
|
-
async sendToQueue(queue, content, options, customHeaders
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
RabbitMq.validateName('queue', queue);
|
|
476
|
-
await this.assertChannel();
|
|
477
|
-
await this.assertQueue(queue, options);
|
|
478
|
-
const res = await this.channel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
479
|
-
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
480
|
-
return res;
|
|
481
|
-
}
|
|
482
|
-
catch (e) {
|
|
483
|
-
logger_1.default.error(`rabbit: failed to send to queue ${queue}`, { e });
|
|
484
|
-
throw e;
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
if (isBlocking) {
|
|
488
|
-
return callback();
|
|
531
|
+
async sendToQueue(queue, content, options, customHeaders) {
|
|
532
|
+
try {
|
|
533
|
+
await this.assertChannel({ connectionPurpose: consts_1.ConnectionPurpose.Publish });
|
|
489
534
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
const connection = await this.getConnection();
|
|
494
|
-
const isConnected = connection.isConnected();
|
|
495
|
-
if (!isConnected) {
|
|
496
|
-
logger_1.default.error('rabbit: isConnected - false');
|
|
497
|
-
return false;
|
|
535
|
+
catch (e) {
|
|
536
|
+
logger_1.default.error(`rabbit sendToQueue: failed to send assert channel when sending to queue ${queue}`, { e });
|
|
537
|
+
throw e;
|
|
498
538
|
}
|
|
499
|
-
const channel = await this.assertChannel();
|
|
500
539
|
try {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
...this.consumers.map((c) => channel.checkQueue(c.queue)),
|
|
504
|
-
]);
|
|
540
|
+
RabbitMq.validateName('queue', queue);
|
|
541
|
+
await this.assertQueue(queue, options);
|
|
505
542
|
}
|
|
506
543
|
catch (e) {
|
|
507
|
-
logger_1.default.error(
|
|
508
|
-
|
|
544
|
+
logger_1.default.error(`rabbit sendToQueue: failed to assert queue ${queue}`, { e });
|
|
545
|
+
throw e;
|
|
546
|
+
}
|
|
547
|
+
try {
|
|
548
|
+
const res = await this.publishChannel?.sendToQueue(queue, Buffer.from(JSON.stringify(content)), RabbitMq.getPublishOptions(customHeaders));
|
|
549
|
+
debug(`rabbit: sending to queue ${queue}`, { res });
|
|
550
|
+
return res;
|
|
551
|
+
}
|
|
552
|
+
catch (e) {
|
|
553
|
+
logger_1.default.error(`rabbit sendToQueue: failed to send to queue ${queue}`, { e });
|
|
554
|
+
throw e;
|
|
509
555
|
}
|
|
510
|
-
|
|
511
|
-
|
|
556
|
+
}
|
|
557
|
+
async isConnected() {
|
|
558
|
+
debug('rabbit: start is connected');
|
|
559
|
+
const isEachConnectionConnected = await Promise.all(Object.entries(this.connectionsMap).map(async ([connectionPurpose, connectionData]) => {
|
|
560
|
+
const { connection } = connectionData;
|
|
561
|
+
if (connection) {
|
|
562
|
+
const isConnected = connection.isConnected();
|
|
563
|
+
if (!isConnected) {
|
|
564
|
+
logger_1.default.error('rabbit: isConnected - false', { connectionPurpose });
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
logger_1.default.info('rabbit: isConnected - true', { connectionPurpose });
|
|
568
|
+
if (connectionPurpose === consts_1.ConnectionPurpose.Publish) {
|
|
569
|
+
const channel = await this.assertChannel({ connectionPurpose: connectionPurpose });
|
|
570
|
+
try {
|
|
571
|
+
await Promise.all([
|
|
572
|
+
channel.waitForConnect(),
|
|
573
|
+
...this.consumers.map((c) => channel.checkQueue(c.queue)),
|
|
574
|
+
]);
|
|
575
|
+
}
|
|
576
|
+
catch (e) {
|
|
577
|
+
logger_1.default.error('rabbit: isConnected - false');
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
logger_1.default.info('rabbit: connection hasnt initialized yet', { connectionPurpose });
|
|
584
|
+
}
|
|
585
|
+
return true;
|
|
586
|
+
}));
|
|
587
|
+
return isEachConnectionConnected.every((isConnected) => isConnected === true);
|
|
512
588
|
}
|
|
513
589
|
async gracefulShutdown(signal) {
|
|
514
590
|
const tagsNumber = this.consumersTags.length;
|
|
@@ -527,3 +603,5 @@ class RabbitMq {
|
|
|
527
603
|
}
|
|
528
604
|
}
|
|
529
605
|
exports.default = RabbitMq;
|
|
606
|
+
var celery_1 = require("./lib/celery");
|
|
607
|
+
Object.defineProperty(exports, "sendCeleryTaskViaHttp", { enumerable: true, get: function () { return celery_1.sendCeleryTaskViaHttp; } });
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface TaskData {
|
|
2
|
+
[key: string]: any;
|
|
3
|
+
}
|
|
4
|
+
interface SendTaskOptions {
|
|
5
|
+
taskName: string;
|
|
6
|
+
queueName: string;
|
|
7
|
+
}
|
|
8
|
+
declare function sendCeleryTaskViaHttp(data: TaskData, { taskName, queueName }: SendTaskOptions): Promise<void>;
|
|
9
|
+
export { sendCeleryTaskViaHttp, TaskData, SendTaskOptions };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.sendCeleryTaskViaHttp = void 0;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const logger_1 = __importDefault(require("../logger"));
|
|
9
|
+
// Environment configuration
|
|
10
|
+
const config = {
|
|
11
|
+
host: process.env.RABBITMQ_SERVICE_HOST || 'localhost',
|
|
12
|
+
username: process.env.RABBITMQ_USERNAME || 'guest',
|
|
13
|
+
password: process.env.RABBITMQ_PASSWORD || 'guest',
|
|
14
|
+
};
|
|
15
|
+
async function sendCeleryTaskViaHttp(data, { taskName, queueName }) {
|
|
16
|
+
const apiUrl = `http://${config.host}:15672/api/exchanges/%2f/amq.default/publish`;
|
|
17
|
+
const message = {
|
|
18
|
+
task: taskName,
|
|
19
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
20
|
+
args: [data],
|
|
21
|
+
};
|
|
22
|
+
const payload = {
|
|
23
|
+
properties: {
|
|
24
|
+
delivery_mode: 2,
|
|
25
|
+
content_type: 'application/json',
|
|
26
|
+
},
|
|
27
|
+
routing_key: queueName,
|
|
28
|
+
payload: JSON.stringify(message),
|
|
29
|
+
payload_encoding: 'string',
|
|
30
|
+
};
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch(apiUrl, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': 'application/json',
|
|
36
|
+
Authorization: `Basic ${Buffer.from(`${config.username}:${config.password}`).toString('base64')}`,
|
|
37
|
+
},
|
|
38
|
+
body: JSON.stringify(payload),
|
|
39
|
+
});
|
|
40
|
+
if (response.ok) {
|
|
41
|
+
const result = await response.json();
|
|
42
|
+
logger_1.default.info('Successfully published message:', result);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
logger_1.default.error(`Failed to publish message. Status code: ${response.status}`);
|
|
46
|
+
logger_1.default.error(`Response: ${await response.text()}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
logger_1.default.error('Error sending request:', error instanceof Error ? error.message : String(error));
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.sendCeleryTaskViaHttp = sendCeleryTaskViaHttp;
|
package/dist/lib/consts.d.ts
CHANGED
|
@@ -3,10 +3,9 @@ export declare const DEFAULT_LOCK_TIMEOUT: number;
|
|
|
3
3
|
export declare const RETRY_HEADER = "x-retry-count";
|
|
4
4
|
export declare const TRACING_HEADER = "x-trace-id";
|
|
5
5
|
export declare const USER_TRACING_HEADER = "x-af-user-id";
|
|
6
|
+
export declare const AUTOMATION_ID_HEADER = "x-af-automation-id";
|
|
6
7
|
export declare const USER_OBJECT = "userObject";
|
|
7
8
|
export declare const DEFAULT_USE_CONSUME_WITH_LOCK = false;
|
|
8
|
-
export declare const CONNECTION_CREATED_CONST = "connectionCreated";
|
|
9
|
-
export declare const CONNECTION_FAILED_CONST = "connectionFailed";
|
|
10
9
|
export declare const DEFAULT_OPTIONS: {
|
|
11
10
|
limit: number;
|
|
12
11
|
retries: number;
|
|
@@ -16,3 +15,7 @@ export declare const DEFAULT_OPTIONS: {
|
|
|
16
15
|
auditContext: null;
|
|
17
16
|
enableRabbitTrace: boolean;
|
|
18
17
|
};
|
|
18
|
+
export declare enum ConnectionPurpose {
|
|
19
|
+
Consume = "consume",
|
|
20
|
+
Publish = "publish"
|
|
21
|
+
}
|