@zdavison/matador 2.0.2 → 2.0.4
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/core/matador.d.ts +2 -2
- package/dist/core/matador.d.ts.map +1 -1
- package/dist/core/matador.js +6 -0
- package/dist/index.cjs +80 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/pipeline.d.ts +4 -1
- package/dist/pipeline/pipeline.d.ts.map +1 -1
- package/dist/pipeline/pipeline.js +16 -4
- package/dist/pipeline/pipeline.test.js +15 -1
- package/dist/topology/builder.d.ts +14 -0
- package/dist/topology/builder.d.ts.map +1 -1
- package/dist/topology/builder.js +20 -11
- package/dist/topology/builder.test.js +138 -0
- package/dist/topology/index.d.ts +2 -2
- package/dist/topology/index.d.ts.map +1 -1
- package/dist/topology/index.js +1 -1
- package/dist/topology/types.d.ts +53 -1
- package/dist/topology/types.d.ts.map +1 -1
- package/dist/topology/types.js +10 -0
- package/dist/transport/local/local-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +8 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
- package/dist/transport/rabbitmq/rabbitmq-transport.js +42 -22
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +2 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +1 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.test.js +98 -0
- package/dist/types/dispatcher.d.ts +14 -0
- package/dist/types/dispatcher.d.ts.map +1 -0
- package/dist/types/dispatcher.js +1 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/subscriber.d.ts +20 -4
- package/dist/types/subscriber.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/matador.ts +14 -1
- package/src/index.ts +9 -0
- package/src/pipeline/pipeline.test.ts +24 -2
- package/src/pipeline/pipeline.ts +28 -6
- package/src/topology/builder.test.ts +176 -0
- package/src/topology/builder.ts +44 -5
- package/src/topology/index.ts +4 -0
- package/src/topology/types.ts +75 -1
- package/src/transport/local/local-transport.ts +4 -1
- package/src/transport/rabbitmq/rabbitmq-transport.test.ts +118 -0
- package/src/transport/rabbitmq/rabbitmq-transport.ts +48 -25
- package/src/types/dispatcher.ts +18 -0
- package/src/types/index.ts +4 -0
- package/src/types/subscriber.ts +22 -3
- package/test/e2e/rabbitmq-transport.e2e.test.ts +287 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
RabbitMQContainer,
|
|
12
12
|
type StartedRabbitMQContainer,
|
|
13
13
|
} from '@testcontainers/rabbitmq';
|
|
14
|
+
import type { Topology } from '../../src/topology/types.js';
|
|
14
15
|
import type { Subscription } from '../../src/transport/index.js';
|
|
15
16
|
import { RabbitMQTransport } from '../../src/transport/rabbitmq/rabbitmq-transport.js';
|
|
16
17
|
import {
|
|
@@ -221,6 +222,292 @@ describe.skipIf(SKIP_E2E)('RabbitMQ Transport E2E', () => {
|
|
|
221
222
|
});
|
|
222
223
|
});
|
|
223
224
|
|
|
225
|
+
describe('exact queue definitions with transport options', () => {
|
|
226
|
+
let transport: RabbitMQTransport;
|
|
227
|
+
const subscriptions: Subscription[] = [];
|
|
228
|
+
|
|
229
|
+
beforeEach(async () => {
|
|
230
|
+
transport = new RabbitMQTransport({
|
|
231
|
+
url: connectionUrl,
|
|
232
|
+
quorumQueues: false,
|
|
233
|
+
defaultPrefetch: 5,
|
|
234
|
+
});
|
|
235
|
+
await transport.connect();
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
afterEach(async () => {
|
|
239
|
+
for (const sub of subscriptions) {
|
|
240
|
+
if (sub.isActive) {
|
|
241
|
+
await sub.unsubscribe();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (transport.isConnected()) {
|
|
245
|
+
await transport.disconnect();
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should apply exact RabbitMQ options when asserting queue', async () => {
|
|
250
|
+
const namespace = `exact-opts-${Date.now()}`;
|
|
251
|
+
const exactQueueName = `${namespace}.shared.custom-queue`;
|
|
252
|
+
|
|
253
|
+
const topology: Topology = {
|
|
254
|
+
namespace,
|
|
255
|
+
queues: [
|
|
256
|
+
{
|
|
257
|
+
name: exactQueueName,
|
|
258
|
+
exact: true,
|
|
259
|
+
transport: {
|
|
260
|
+
rabbitmq: {
|
|
261
|
+
options: {
|
|
262
|
+
durable: true,
|
|
263
|
+
arguments: {
|
|
264
|
+
'x-max-length': 1000,
|
|
265
|
+
'x-message-ttl': 60000,
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
deadLetter: {
|
|
273
|
+
unhandled: { enabled: false },
|
|
274
|
+
undeliverable: { enabled: false },
|
|
275
|
+
},
|
|
276
|
+
retry: {
|
|
277
|
+
enabled: true,
|
|
278
|
+
defaultDelayMs: 1000,
|
|
279
|
+
maxDelayMs: 30000,
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// Should not throw - queue options should be applied correctly
|
|
284
|
+
await transport.applyTopology(topology);
|
|
285
|
+
|
|
286
|
+
// Verify queue works by sending and receiving a message
|
|
287
|
+
let receivedMessage = false;
|
|
288
|
+
const subscription = await transport.subscribe(
|
|
289
|
+
exactQueueName,
|
|
290
|
+
async (_env, receipt) => {
|
|
291
|
+
receivedMessage = true;
|
|
292
|
+
await transport.complete(receipt);
|
|
293
|
+
},
|
|
294
|
+
);
|
|
295
|
+
subscriptions.push(subscription);
|
|
296
|
+
|
|
297
|
+
await transport.send(exactQueueName, createTestEnvelope());
|
|
298
|
+
await waitFor(() => receivedMessage, 5000);
|
|
299
|
+
|
|
300
|
+
expect(receivedMessage).toBe(true);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should create retry queue for exact queue when retry is enabled', async () => {
|
|
304
|
+
const namespace = `exact-retry-${Date.now()}`;
|
|
305
|
+
const exactQueueName = `${namespace}.shared.retry-queue`;
|
|
306
|
+
|
|
307
|
+
const topology: Topology = {
|
|
308
|
+
namespace,
|
|
309
|
+
queues: [
|
|
310
|
+
{
|
|
311
|
+
name: exactQueueName,
|
|
312
|
+
exact: true,
|
|
313
|
+
transport: {
|
|
314
|
+
rabbitmq: {
|
|
315
|
+
options: {
|
|
316
|
+
durable: true,
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
deadLetter: {
|
|
323
|
+
unhandled: { enabled: false },
|
|
324
|
+
undeliverable: { enabled: false },
|
|
325
|
+
},
|
|
326
|
+
retry: {
|
|
327
|
+
enabled: true,
|
|
328
|
+
defaultDelayMs: 1000,
|
|
329
|
+
maxDelayMs: 30000,
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// Apply topology - should create both work queue and retry queue
|
|
334
|
+
await transport.applyTopology(topology);
|
|
335
|
+
|
|
336
|
+
// Verify the main queue works
|
|
337
|
+
let receivedCount = 0;
|
|
338
|
+
const subscription = await transport.subscribe(
|
|
339
|
+
exactQueueName,
|
|
340
|
+
async (_env, receipt) => {
|
|
341
|
+
receivedCount++;
|
|
342
|
+
await transport.complete(receipt);
|
|
343
|
+
},
|
|
344
|
+
);
|
|
345
|
+
subscriptions.push(subscription);
|
|
346
|
+
|
|
347
|
+
await transport.send(exactQueueName, createTestEnvelope());
|
|
348
|
+
await waitFor(() => receivedCount === 1, 5000);
|
|
349
|
+
|
|
350
|
+
expect(receivedCount).toBe(1);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it('should use custom dead letter exchange from exact options', async () => {
|
|
354
|
+
const namespace = `exact-dlx-${Date.now()}`;
|
|
355
|
+
const exactQueueName = `${namespace}.shared.dlx-queue`;
|
|
356
|
+
const customDlxExchange = `${namespace}.custom-dlx`;
|
|
357
|
+
|
|
358
|
+
// First create the custom DLX exchange manually
|
|
359
|
+
// (In real usage, this would be managed externally)
|
|
360
|
+
|
|
361
|
+
const topology: Topology = {
|
|
362
|
+
namespace,
|
|
363
|
+
queues: [
|
|
364
|
+
{
|
|
365
|
+
name: exactQueueName,
|
|
366
|
+
exact: true,
|
|
367
|
+
transport: {
|
|
368
|
+
rabbitmq: {
|
|
369
|
+
options: {
|
|
370
|
+
durable: true,
|
|
371
|
+
deadLetterExchange: customDlxExchange,
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
],
|
|
377
|
+
deadLetter: {
|
|
378
|
+
unhandled: { enabled: false },
|
|
379
|
+
undeliverable: { enabled: false },
|
|
380
|
+
},
|
|
381
|
+
retry: {
|
|
382
|
+
enabled: false,
|
|
383
|
+
defaultDelayMs: 1000,
|
|
384
|
+
maxDelayMs: 30000,
|
|
385
|
+
},
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// Should not throw - custom DLX should be set
|
|
389
|
+
await transport.applyTopology(topology);
|
|
390
|
+
|
|
391
|
+
// Verify queue works
|
|
392
|
+
let receivedMessage = false;
|
|
393
|
+
const subscription = await transport.subscribe(
|
|
394
|
+
exactQueueName,
|
|
395
|
+
async (_env, receipt) => {
|
|
396
|
+
receivedMessage = true;
|
|
397
|
+
await transport.complete(receipt);
|
|
398
|
+
},
|
|
399
|
+
);
|
|
400
|
+
subscriptions.push(subscription);
|
|
401
|
+
|
|
402
|
+
await transport.send(exactQueueName, createTestEnvelope());
|
|
403
|
+
await waitFor(() => receivedMessage, 5000);
|
|
404
|
+
|
|
405
|
+
expect(receivedMessage).toBe(true);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
it('should apply RabbitMQ options without exact mode', async () => {
|
|
409
|
+
const namespace = `opts-no-exact-${Date.now()}`;
|
|
410
|
+
|
|
411
|
+
const topology: Topology = {
|
|
412
|
+
namespace,
|
|
413
|
+
queues: [
|
|
414
|
+
{
|
|
415
|
+
name: 'custom-options-queue',
|
|
416
|
+
// exact: false (default) - namespace prefix will be added
|
|
417
|
+
transport: {
|
|
418
|
+
rabbitmq: {
|
|
419
|
+
options: {
|
|
420
|
+
durable: true,
|
|
421
|
+
arguments: {
|
|
422
|
+
'x-max-length': 500,
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
],
|
|
429
|
+
deadLetter: {
|
|
430
|
+
unhandled: { enabled: false },
|
|
431
|
+
undeliverable: { enabled: false },
|
|
432
|
+
},
|
|
433
|
+
retry: {
|
|
434
|
+
enabled: true,
|
|
435
|
+
defaultDelayMs: 1000,
|
|
436
|
+
maxDelayMs: 30000,
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
await transport.applyTopology(topology);
|
|
441
|
+
|
|
442
|
+
// Queue name should have namespace prefix since exact: false
|
|
443
|
+
const queueName = `${namespace}.custom-options-queue`;
|
|
444
|
+
|
|
445
|
+
let receivedMessage = false;
|
|
446
|
+
const subscription = await transport.subscribe(
|
|
447
|
+
queueName,
|
|
448
|
+
async (_env, receipt) => {
|
|
449
|
+
receivedMessage = true;
|
|
450
|
+
await transport.complete(receipt);
|
|
451
|
+
},
|
|
452
|
+
);
|
|
453
|
+
subscriptions.push(subscription);
|
|
454
|
+
|
|
455
|
+
await transport.send(queueName, createTestEnvelope());
|
|
456
|
+
await waitFor(() => receivedMessage, 5000);
|
|
457
|
+
|
|
458
|
+
expect(receivedMessage).toBe(true);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('should allow queue name with dots when exact: true', async () => {
|
|
462
|
+
const namespace = `exact-dots-${Date.now()}`;
|
|
463
|
+
const exactQueueName = 'matador.shared.id-platform.events';
|
|
464
|
+
|
|
465
|
+
const topology: Topology = {
|
|
466
|
+
namespace,
|
|
467
|
+
queues: [
|
|
468
|
+
{
|
|
469
|
+
name: exactQueueName,
|
|
470
|
+
exact: true,
|
|
471
|
+
transport: {
|
|
472
|
+
rabbitmq: {
|
|
473
|
+
options: {
|
|
474
|
+
durable: true,
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
],
|
|
480
|
+
deadLetter: {
|
|
481
|
+
unhandled: { enabled: false },
|
|
482
|
+
undeliverable: { enabled: false },
|
|
483
|
+
},
|
|
484
|
+
retry: {
|
|
485
|
+
enabled: true,
|
|
486
|
+
defaultDelayMs: 1000,
|
|
487
|
+
maxDelayMs: 30000,
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
await transport.applyTopology(topology);
|
|
492
|
+
|
|
493
|
+
// Verify queue with dots in name works
|
|
494
|
+
let receivedMessage = false;
|
|
495
|
+
const subscription = await transport.subscribe(
|
|
496
|
+
exactQueueName,
|
|
497
|
+
async (_env, receipt) => {
|
|
498
|
+
receivedMessage = true;
|
|
499
|
+
await transport.complete(receipt);
|
|
500
|
+
},
|
|
501
|
+
);
|
|
502
|
+
subscriptions.push(subscription);
|
|
503
|
+
|
|
504
|
+
await transport.send(exactQueueName, createTestEnvelope());
|
|
505
|
+
await waitFor(() => receivedMessage, 5000);
|
|
506
|
+
|
|
507
|
+
expect(receivedMessage).toBe(true);
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
|
|
224
511
|
describe('channel per queue isolation', () => {
|
|
225
512
|
let transport: RabbitMQTransport;
|
|
226
513
|
const subscriptions: Subscription[] = [];
|