@zdavison/matador 2.0.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/cli.ts +453 -0
- package/dist/checkpoint/context.d.ts +59 -0
- package/dist/checkpoint/context.d.ts.map +1 -0
- package/dist/checkpoint/context.js +140 -0
- package/dist/checkpoint/context.test.d.ts +2 -0
- package/dist/checkpoint/context.test.d.ts.map +1 -0
- package/dist/checkpoint/context.test.js +424 -0
- package/dist/checkpoint/index.d.ts +7 -0
- package/dist/checkpoint/index.d.ts.map +1 -0
- package/dist/checkpoint/index.js +6 -0
- package/dist/checkpoint/stores/memory.d.ts +29 -0
- package/dist/checkpoint/stores/memory.d.ts.map +1 -0
- package/dist/checkpoint/stores/memory.js +39 -0
- package/dist/checkpoint/stores/noop.d.ts +14 -0
- package/dist/checkpoint/stores/noop.d.ts.map +1 -0
- package/dist/checkpoint/stores/noop.js +18 -0
- package/dist/checkpoint/stores/stores.test.d.ts +2 -0
- package/dist/checkpoint/stores/stores.test.d.ts.map +1 -0
- package/dist/checkpoint/stores/stores.test.js +146 -0
- package/dist/checkpoint/types.d.ts +119 -0
- package/dist/checkpoint/types.d.ts.map +1 -0
- package/dist/checkpoint/types.js +1 -0
- package/dist/codec/codec.d.ts +29 -0
- package/dist/codec/codec.d.ts.map +1 -0
- package/dist/codec/codec.js +15 -0
- package/dist/codec/header-aware-codec.d.ts +36 -0
- package/dist/codec/header-aware-codec.d.ts.map +1 -0
- package/dist/codec/header-aware-codec.js +1 -0
- package/dist/codec/index.d.ts +6 -0
- package/dist/codec/index.d.ts.map +1 -0
- package/dist/codec/index.js +3 -0
- package/dist/codec/json-codec.d.ts +13 -0
- package/dist/codec/json-codec.d.ts.map +1 -0
- package/dist/codec/json-codec.js +64 -0
- package/dist/codec/rabbitmq-codec.d.ts +28 -0
- package/dist/codec/rabbitmq-codec.d.ts.map +1 -0
- package/dist/codec/rabbitmq-codec.js +242 -0
- package/dist/codec/rabbitmq-codec.test.d.ts +2 -0
- package/dist/codec/rabbitmq-codec.test.d.ts.map +1 -0
- package/dist/codec/rabbitmq-codec.test.js +433 -0
- package/dist/core/fanout.d.ts +59 -0
- package/dist/core/fanout.d.ts.map +1 -0
- package/dist/core/fanout.js +121 -0
- package/dist/core/fanout.test.d.ts +2 -0
- package/dist/core/fanout.test.d.ts.map +1 -0
- package/dist/core/fanout.test.js +1055 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/matador.d.ts +133 -0
- package/dist/core/matador.d.ts.map +1 -0
- package/dist/core/matador.js +222 -0
- package/dist/core/matador.test.d.ts +2 -0
- package/dist/core/matador.test.d.ts.map +1 -0
- package/dist/core/matador.test.js +446 -0
- package/dist/core/shutdown.d.ts +78 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +111 -0
- package/dist/core/shutdown.test.d.ts +2 -0
- package/dist/core/shutdown.test.d.ts.map +1 -0
- package/dist/core/shutdown.test.js +524 -0
- package/dist/errors/checkpoint-errors.d.ts +30 -0
- package/dist/errors/checkpoint-errors.d.ts.map +1 -0
- package/dist/errors/checkpoint-errors.js +49 -0
- package/dist/errors/has-description.d.ts +18 -0
- package/dist/errors/has-description.d.ts.map +1 -0
- package/dist/errors/has-description.js +9 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +22 -0
- package/dist/errors/matador-errors.d.ts +183 -0
- package/dist/errors/matador-errors.d.ts.map +1 -0
- package/dist/errors/matador-errors.js +376 -0
- package/dist/errors/retry-errors.d.ts +67 -0
- package/dist/errors/retry-errors.d.ts.map +1 -0
- package/dist/errors/retry-errors.js +108 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/safe-hooks.d.ts +34 -0
- package/dist/hooks/safe-hooks.d.ts.map +1 -0
- package/dist/hooks/safe-hooks.js +135 -0
- package/dist/hooks/types.d.ts +189 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +9 -0
- package/dist/index.cjs +3156 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/index.d.ts +3 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/pipeline/pipeline.d.ts +60 -0
- package/dist/pipeline/pipeline.d.ts.map +1 -0
- package/dist/pipeline/pipeline.js +261 -0
- package/dist/pipeline/pipeline.test.d.ts +2 -0
- package/dist/pipeline/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline/pipeline.test.js +1065 -0
- package/dist/retry/index.d.ts +4 -0
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/retry/index.js +1 -0
- package/dist/retry/policy.d.ts +43 -0
- package/dist/retry/policy.d.ts.map +1 -0
- package/dist/retry/policy.js +1 -0
- package/dist/retry/standard-policy.d.ts +44 -0
- package/dist/retry/standard-policy.d.ts.map +1 -0
- package/dist/retry/standard-policy.js +102 -0
- package/dist/retry/standard-policy.test.d.ts +2 -0
- package/dist/retry/standard-policy.test.d.ts.map +1 -0
- package/dist/retry/standard-policy.test.js +190 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/registry.d.ts +63 -0
- package/dist/schema/registry.d.ts.map +1 -0
- package/dist/schema/registry.js +171 -0
- package/dist/schema/registry.test.d.ts +2 -0
- package/dist/schema/registry.test.d.ts.map +1 -0
- package/dist/schema/registry.test.js +278 -0
- package/dist/schema/types.d.ts +158 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +74 -0
- package/dist/schema/types.test.d.ts +2 -0
- package/dist/schema/types.test.d.ts.map +1 -0
- package/dist/schema/types.test.js +243 -0
- package/dist/topology/builder.d.ts +66 -0
- package/dist/topology/builder.d.ts.map +1 -0
- package/dist/topology/builder.js +156 -0
- package/dist/topology/builder.test.d.ts +2 -0
- package/dist/topology/builder.test.d.ts.map +1 -0
- package/dist/topology/builder.test.js +222 -0
- package/dist/topology/index.d.ts +5 -0
- package/dist/topology/index.d.ts.map +1 -0
- package/dist/topology/index.js +2 -0
- package/dist/topology/types.d.ts +76 -0
- package/dist/topology/types.d.ts.map +1 -0
- package/dist/topology/types.js +18 -0
- package/dist/transport/capabilities.d.ts +66 -0
- package/dist/transport/capabilities.d.ts.map +1 -0
- package/dist/transport/capabilities.js +18 -0
- package/dist/transport/connection-manager.d.ts +95 -0
- package/dist/transport/connection-manager.d.ts.map +1 -0
- package/dist/transport/connection-manager.js +144 -0
- package/dist/transport/index.d.ts +11 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +5 -0
- package/dist/transport/local/local-transport.d.ts +62 -0
- package/dist/transport/local/local-transport.d.ts.map +1 -0
- package/dist/transport/local/local-transport.js +241 -0
- package/dist/transport/local/local-transport.test.d.ts +2 -0
- package/dist/transport/local/local-transport.test.d.ts.map +1 -0
- package/dist/transport/local/local-transport.test.js +192 -0
- package/dist/transport/multi/multi-transport.d.ts +94 -0
- package/dist/transport/multi/multi-transport.d.ts.map +1 -0
- package/dist/transport/multi/multi-transport.js +184 -0
- package/dist/transport/multi/multi-transport.test.d.ts +2 -0
- package/dist/transport/multi/multi-transport.test.d.ts.map +1 -0
- package/dist/transport/multi/multi-transport.test.js +236 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +73 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.js +518 -0
- package/dist/transport/transport.d.ts +152 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +1 -0
- package/dist/types/common.d.ts +41 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +12 -0
- package/dist/types/envelope.d.ts +82 -0
- package/dist/types/envelope.d.ts.map +1 -0
- package/dist/types/envelope.js +36 -0
- package/dist/types/event.d.ts +96 -0
- package/dist/types/event.d.ts.map +1 -0
- package/dist/types/event.js +26 -0
- package/dist/types/event.test.d.ts +2 -0
- package/dist/types/event.test.d.ts.map +1 -0
- package/dist/types/event.test.js +130 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/subscriber.d.ts +207 -0
- package/dist/types/subscriber.d.ts.map +1 -0
- package/dist/types/subscriber.js +99 -0
- package/examples/config.ts +126 -0
- package/examples/event.ts +26 -0
- package/examples/order-event.json +19 -0
- package/package.json +66 -0
- package/src/checkpoint/context.test.ts +510 -0
- package/src/checkpoint/context.ts +213 -0
- package/src/checkpoint/index.ts +30 -0
- package/src/checkpoint/stores/memory.ts +47 -0
- package/src/checkpoint/stores/noop.ts +22 -0
- package/src/checkpoint/stores/stores.test.ts +177 -0
- package/src/checkpoint/types.ts +147 -0
- package/src/codec/codec.ts +42 -0
- package/src/codec/header-aware-codec.ts +41 -0
- package/src/codec/index.ts +11 -0
- package/src/codec/json-codec.ts +69 -0
- package/src/codec/rabbitmq-codec.test.ts +516 -0
- package/src/codec/rabbitmq-codec.ts +336 -0
- package/src/core/fanout.test.ts +1351 -0
- package/src/core/fanout.ts +184 -0
- package/src/core/index.ts +12 -0
- package/src/core/matador.test.ts +575 -0
- package/src/core/matador.ts +344 -0
- package/src/core/shutdown.test.ts +853 -0
- package/src/core/shutdown.ts +165 -0
- package/src/errors/checkpoint-errors.ts +62 -0
- package/src/errors/has-description.ts +25 -0
- package/src/errors/index.ts +57 -0
- package/src/errors/matador-errors.ts +477 -0
- package/src/errors/retry-errors.ts +134 -0
- package/src/hooks/index.ts +15 -0
- package/src/hooks/safe-hooks.ts +223 -0
- package/src/hooks/types.ts +248 -0
- package/src/index.ts +231 -0
- package/src/pipeline/index.ts +2 -0
- package/src/pipeline/pipeline.test.ts +1317 -0
- package/src/pipeline/pipeline.ts +371 -0
- package/src/retry/index.ts +4 -0
- package/src/retry/policy.ts +46 -0
- package/src/retry/standard-policy.test.ts +282 -0
- package/src/retry/standard-policy.ts +156 -0
- package/src/schema/index.ts +16 -0
- package/src/schema/registry.test.ts +339 -0
- package/src/schema/registry.ts +226 -0
- package/src/schema/types.test.ts +281 -0
- package/src/schema/types.ts +217 -0
- package/src/topology/builder.test.ts +275 -0
- package/src/topology/builder.ts +199 -0
- package/src/topology/index.ts +15 -0
- package/src/topology/types.ts +109 -0
- package/src/transport/capabilities.ts +88 -0
- package/src/transport/connection-manager.ts +218 -0
- package/src/transport/index.ts +42 -0
- package/src/transport/local/local-transport.test.ts +262 -0
- package/src/transport/local/local-transport.ts +327 -0
- package/src/transport/multi/multi-transport.test.ts +320 -0
- package/src/transport/multi/multi-transport.ts +294 -0
- package/src/transport/rabbitmq/rabbitmq-transport.ts +753 -0
- package/src/transport/transport.ts +200 -0
- package/src/types/common.ts +53 -0
- package/src/types/envelope.ts +152 -0
- package/src/types/event.test.ts +157 -0
- package/src/types/event.ts +118 -0
- package/src/types/index.ts +52 -0
- package/src/types/subscriber.ts +310 -0
- package/test/e2e/multi-transport.e2e.test.ts +236 -0
- package/test/e2e/rabbitmq-transport.e2e.test.ts +327 -0
- package/test/e2e/transport-compliance.e2e.test.ts +505 -0
- package/test/integration/matador.integration.test.ts +634 -0
- package/tsconfig.json +29 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsup.config.ts +13 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { CodecDecodeError } from './codec.js';
|
|
2
|
+
/**
|
|
3
|
+
* Header names used by Matador in RabbitMQ messages.
|
|
4
|
+
*/
|
|
5
|
+
const HEADERS = {
|
|
6
|
+
// Routing
|
|
7
|
+
EVENT_KEY: 'x-matador-event-key',
|
|
8
|
+
TARGET_SUBSCRIBER: 'x-matador-subscriber',
|
|
9
|
+
SCHEDULED_FOR: 'x-matador-scheduled-for',
|
|
10
|
+
ORIGINAL_QUEUE: 'x-matador-original-queue',
|
|
11
|
+
// Processing state
|
|
12
|
+
ATTEMPTS: 'x-matador-attempts',
|
|
13
|
+
CREATED_AT: 'x-matador-created-at',
|
|
14
|
+
FIRST_ERROR: 'x-matador-first-error',
|
|
15
|
+
LAST_ERROR: 'x-matador-last-error',
|
|
16
|
+
// Observability
|
|
17
|
+
IMPORTANCE: 'x-matador-importance',
|
|
18
|
+
CORRELATION_ID: 'x-matador-correlation-id',
|
|
19
|
+
// v1 compatibility headers
|
|
20
|
+
V1_EVENT_ID: 'x-event-id',
|
|
21
|
+
V1_CORRELATION_ID: 'x-correlation-id',
|
|
22
|
+
V1_USER_ID: 'x-user-id',
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* RabbitMQ-specific codec that stores routing/metadata in headers.
|
|
26
|
+
*
|
|
27
|
+
* Benefits:
|
|
28
|
+
* - No duplication between body and headers
|
|
29
|
+
* - RabbitMQ can route/filter based on headers
|
|
30
|
+
* - Smaller message body
|
|
31
|
+
* - v1 message format compatibility built-in
|
|
32
|
+
*/
|
|
33
|
+
export class RabbitMQCodec {
|
|
34
|
+
contentType = 'application/json';
|
|
35
|
+
encode(envelope) {
|
|
36
|
+
const { docket } = envelope;
|
|
37
|
+
// Body only contains id, data, and metadata (since metadata can be large)
|
|
38
|
+
const body = {
|
|
39
|
+
id: envelope.id,
|
|
40
|
+
data: envelope.data,
|
|
41
|
+
...(docket.metadata !== undefined && { metadata: docket.metadata }),
|
|
42
|
+
};
|
|
43
|
+
// All docket fields go in headers
|
|
44
|
+
const headers = {
|
|
45
|
+
// Routing
|
|
46
|
+
[HEADERS.EVENT_KEY]: docket.eventKey,
|
|
47
|
+
[HEADERS.TARGET_SUBSCRIBER]: docket.targetSubscriber,
|
|
48
|
+
// Processing state
|
|
49
|
+
[HEADERS.ATTEMPTS]: docket.attempts,
|
|
50
|
+
[HEADERS.CREATED_AT]: docket.createdAt,
|
|
51
|
+
// Observability
|
|
52
|
+
[HEADERS.IMPORTANCE]: docket.importance,
|
|
53
|
+
};
|
|
54
|
+
// Optional fields
|
|
55
|
+
if (docket.scheduledFor !== undefined) {
|
|
56
|
+
headers[HEADERS.SCHEDULED_FOR] = docket.scheduledFor;
|
|
57
|
+
}
|
|
58
|
+
if (docket.originalQueue !== undefined) {
|
|
59
|
+
headers[HEADERS.ORIGINAL_QUEUE] = docket.originalQueue;
|
|
60
|
+
}
|
|
61
|
+
if (docket.firstError !== undefined) {
|
|
62
|
+
headers[HEADERS.FIRST_ERROR] = docket.firstError;
|
|
63
|
+
}
|
|
64
|
+
if (docket.lastError !== undefined) {
|
|
65
|
+
headers[HEADERS.LAST_ERROR] = docket.lastError;
|
|
66
|
+
}
|
|
67
|
+
if (docket.correlationId !== undefined) {
|
|
68
|
+
headers[HEADERS.CORRELATION_ID] = docket.correlationId;
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
body: new TextEncoder().encode(JSON.stringify(body)),
|
|
72
|
+
headers,
|
|
73
|
+
contentType: this.contentType,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
decode(body, headers) {
|
|
77
|
+
if (body.length === 0) {
|
|
78
|
+
throw new CodecDecodeError('Cannot decode empty buffer');
|
|
79
|
+
}
|
|
80
|
+
let json;
|
|
81
|
+
try {
|
|
82
|
+
json = new TextDecoder('utf-8').decode(body);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw new CodecDecodeError('Invalid UTF-8 encoding', error);
|
|
86
|
+
}
|
|
87
|
+
let parsed;
|
|
88
|
+
try {
|
|
89
|
+
parsed = JSON.parse(json);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw new CodecDecodeError('Invalid JSON', error);
|
|
93
|
+
}
|
|
94
|
+
// Detect format: v1 has 'key' and 'targetSubscriber' at top level
|
|
95
|
+
if (this.isV1Body(parsed)) {
|
|
96
|
+
return this.decodeV1(parsed, headers);
|
|
97
|
+
}
|
|
98
|
+
// v2 format
|
|
99
|
+
if (!this.isV2Body(parsed)) {
|
|
100
|
+
throw new CodecDecodeError('Invalid message body structure');
|
|
101
|
+
}
|
|
102
|
+
return this.decodeV2(parsed, headers);
|
|
103
|
+
}
|
|
104
|
+
isV1Body(value) {
|
|
105
|
+
if (typeof value !== 'object' || value === null)
|
|
106
|
+
return false;
|
|
107
|
+
const obj = value;
|
|
108
|
+
return (typeof obj['key'] === 'string' &&
|
|
109
|
+
typeof obj['targetSubscriber'] === 'string' &&
|
|
110
|
+
'data' in obj &&
|
|
111
|
+
!('payload' in obj));
|
|
112
|
+
}
|
|
113
|
+
isV2Body(value) {
|
|
114
|
+
if (typeof value !== 'object' || value === null)
|
|
115
|
+
return false;
|
|
116
|
+
const obj = value;
|
|
117
|
+
return (typeof obj['id'] === 'string' && 'data' in obj && !('key' in obj) // Distinguish from v1 which also has 'data'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
decodeV2(body, headers) {
|
|
121
|
+
// Required headers
|
|
122
|
+
const eventKey = this.requireStringHeader(headers, HEADERS.EVENT_KEY);
|
|
123
|
+
const targetSubscriber = this.requireStringHeader(headers, HEADERS.TARGET_SUBSCRIBER);
|
|
124
|
+
const attempts = this.requireNumberHeader(headers, HEADERS.ATTEMPTS);
|
|
125
|
+
const createdAt = this.requireStringHeader(headers, HEADERS.CREATED_AT);
|
|
126
|
+
const importance = this.requireStringHeader(headers, HEADERS.IMPORTANCE);
|
|
127
|
+
// Optional headers
|
|
128
|
+
const scheduledFor = this.optionalStringHeader(headers, HEADERS.SCHEDULED_FOR);
|
|
129
|
+
const originalQueue = this.optionalStringHeader(headers, HEADERS.ORIGINAL_QUEUE);
|
|
130
|
+
const firstError = this.optionalStringHeader(headers, HEADERS.FIRST_ERROR);
|
|
131
|
+
const lastError = this.optionalStringHeader(headers, HEADERS.LAST_ERROR);
|
|
132
|
+
const correlationId = this.optionalStringHeader(headers, HEADERS.CORRELATION_ID);
|
|
133
|
+
const docket = {
|
|
134
|
+
// Routing
|
|
135
|
+
eventKey,
|
|
136
|
+
targetSubscriber,
|
|
137
|
+
...(scheduledFor !== undefined && { scheduledFor }),
|
|
138
|
+
...(originalQueue !== undefined && { originalQueue }),
|
|
139
|
+
// Processing state
|
|
140
|
+
attempts,
|
|
141
|
+
createdAt,
|
|
142
|
+
...(firstError !== undefined && { firstError }),
|
|
143
|
+
...(lastError !== undefined && { lastError }),
|
|
144
|
+
// Observability
|
|
145
|
+
importance: importance,
|
|
146
|
+
...(correlationId !== undefined && { correlationId }),
|
|
147
|
+
...(body.metadata !== undefined && { metadata: body.metadata }),
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
id: body.id,
|
|
151
|
+
data: body.data,
|
|
152
|
+
docket,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Decodes a v1 format message for backwards compatibility.
|
|
157
|
+
* v1 format: { key, data, metadata, universal, targetSubscriber, ... }
|
|
158
|
+
*/
|
|
159
|
+
decodeV1(body, headers) {
|
|
160
|
+
const { event_id, user_id, correlation_id, ...otherUniversal } = body.universal ?? {};
|
|
161
|
+
// Merge metadata: event metadata + remaining universal fields
|
|
162
|
+
const mergedMetadata = {};
|
|
163
|
+
if (body.metadata && typeof body.metadata === 'object') {
|
|
164
|
+
Object.assign(mergedMetadata, body.metadata);
|
|
165
|
+
}
|
|
166
|
+
if (user_id !== undefined && user_id !== null) {
|
|
167
|
+
mergedMetadata['user_id'] = user_id;
|
|
168
|
+
}
|
|
169
|
+
Object.assign(mergedMetadata, otherUniversal);
|
|
170
|
+
// Try to get attempts from header (v1 also uses x-matador-attempts)
|
|
171
|
+
const attempts = typeof headers[HEADERS.ATTEMPTS] === 'number'
|
|
172
|
+
? headers[HEADERS.ATTEMPTS]
|
|
173
|
+
: 1;
|
|
174
|
+
// Try to get importance from header
|
|
175
|
+
const importance = typeof headers[HEADERS.IMPORTANCE] === 'string'
|
|
176
|
+
? headers[HEADERS.IMPORTANCE]
|
|
177
|
+
: 'should-investigate';
|
|
178
|
+
// Get correlation ID from either v1 or v2 header
|
|
179
|
+
const correlationId = headers[HEADERS.CORRELATION_ID] ??
|
|
180
|
+
headers[HEADERS.V1_CORRELATION_ID] ??
|
|
181
|
+
correlation_id ??
|
|
182
|
+
undefined;
|
|
183
|
+
// Get event ID from v1 header or body
|
|
184
|
+
const eventId = headers[HEADERS.V1_EVENT_ID] ??
|
|
185
|
+
event_id ??
|
|
186
|
+
crypto.randomUUID();
|
|
187
|
+
const now = new Date().toISOString();
|
|
188
|
+
// Calculate scheduledFor if delayMs was specified (though it's likely already passed)
|
|
189
|
+
let scheduledFor;
|
|
190
|
+
if (body.options?.delayMs) {
|
|
191
|
+
scheduledFor = new Date(Date.now() + body.options.delayMs).toISOString();
|
|
192
|
+
}
|
|
193
|
+
const docket = {
|
|
194
|
+
// Routing
|
|
195
|
+
eventKey: body.key,
|
|
196
|
+
targetSubscriber: body.targetSubscriber,
|
|
197
|
+
...(scheduledFor !== undefined && { scheduledFor }),
|
|
198
|
+
// Processing state
|
|
199
|
+
attempts,
|
|
200
|
+
createdAt: now,
|
|
201
|
+
// Observability
|
|
202
|
+
importance: importance,
|
|
203
|
+
...(correlationId !== undefined && { correlationId }),
|
|
204
|
+
...(Object.keys(mergedMetadata).length > 0 && {
|
|
205
|
+
metadata: mergedMetadata,
|
|
206
|
+
}),
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
id: eventId,
|
|
210
|
+
data: body.data,
|
|
211
|
+
docket,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
requireStringHeader(headers, name) {
|
|
215
|
+
const value = headers[name];
|
|
216
|
+
if (value === undefined) {
|
|
217
|
+
throw new CodecDecodeError(`Missing required header: ${name}`);
|
|
218
|
+
}
|
|
219
|
+
if (typeof value !== 'string') {
|
|
220
|
+
throw new CodecDecodeError(`Header ${name} must be string, got ${typeof value}`);
|
|
221
|
+
}
|
|
222
|
+
return value;
|
|
223
|
+
}
|
|
224
|
+
requireNumberHeader(headers, name) {
|
|
225
|
+
const value = headers[name];
|
|
226
|
+
if (value === undefined) {
|
|
227
|
+
throw new CodecDecodeError(`Missing required header: ${name}`);
|
|
228
|
+
}
|
|
229
|
+
if (typeof value !== 'number') {
|
|
230
|
+
throw new CodecDecodeError(`Header ${name} must be number, got ${typeof value}`);
|
|
231
|
+
}
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
optionalStringHeader(headers, name) {
|
|
235
|
+
const value = headers[name];
|
|
236
|
+
if (value === undefined)
|
|
237
|
+
return undefined;
|
|
238
|
+
if (typeof value !== 'string')
|
|
239
|
+
return undefined;
|
|
240
|
+
return value;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rabbitmq-codec.test.d.ts","sourceRoot":"","sources":["../../src/codec/rabbitmq-codec.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { RabbitMQCodec } from './rabbitmq-codec.js';
|
|
3
|
+
/**
|
|
4
|
+
* Add your v1 message examples here.
|
|
5
|
+
* Each fixture describes:
|
|
6
|
+
* - name: A descriptive name for the test case
|
|
7
|
+
* - body: The v1 message body (what goes in the RabbitMQ message content)
|
|
8
|
+
* - headers: Optional RabbitMQ headers that accompany the message
|
|
9
|
+
* - expected: The expected v2 envelope fields after translation
|
|
10
|
+
*/
|
|
11
|
+
const V1_MESSAGE_FIXTURES = [
|
|
12
|
+
// === BASIC EXAMPLES ===
|
|
13
|
+
{
|
|
14
|
+
name: 'minimal v1 message with only required fields',
|
|
15
|
+
body: {
|
|
16
|
+
key: 'user.created',
|
|
17
|
+
data: { userId: '123' },
|
|
18
|
+
targetSubscriber: 'email-service',
|
|
19
|
+
},
|
|
20
|
+
expected: {
|
|
21
|
+
eventKey: 'user.created',
|
|
22
|
+
targetSubscriber: 'email-service',
|
|
23
|
+
data: { userId: '123' },
|
|
24
|
+
attempts: 1,
|
|
25
|
+
importance: 'should-investigate',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'v1 message with metadata',
|
|
30
|
+
body: {
|
|
31
|
+
key: 'order.placed',
|
|
32
|
+
data: { orderId: 'ORD-456', amount: 99.99 },
|
|
33
|
+
metadata: { source: 'web', region: 'us-east' },
|
|
34
|
+
targetSubscriber: 'fulfillment-service',
|
|
35
|
+
},
|
|
36
|
+
expected: {
|
|
37
|
+
eventKey: 'order.placed',
|
|
38
|
+
targetSubscriber: 'fulfillment-service',
|
|
39
|
+
data: { orderId: 'ORD-456', amount: 99.99 },
|
|
40
|
+
metadata: { source: 'web', region: 'us-east' },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
// === UNIVERSAL METADATA EXAMPLES ===
|
|
44
|
+
{
|
|
45
|
+
name: 'v1 message with universal.event_id',
|
|
46
|
+
body: {
|
|
47
|
+
key: 'payment.processed',
|
|
48
|
+
data: { paymentId: 'PAY-789' },
|
|
49
|
+
universal: {
|
|
50
|
+
event_id: 'evt-abc-123',
|
|
51
|
+
},
|
|
52
|
+
targetSubscriber: 'notification-service',
|
|
53
|
+
},
|
|
54
|
+
expected: {
|
|
55
|
+
id: 'evt-abc-123',
|
|
56
|
+
eventKey: 'payment.processed',
|
|
57
|
+
targetSubscriber: 'notification-service',
|
|
58
|
+
data: { paymentId: 'PAY-789' },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'v1 message with universal.user_id (merged into metadata)',
|
|
63
|
+
body: {
|
|
64
|
+
key: 'profile.updated',
|
|
65
|
+
data: { field: 'email' },
|
|
66
|
+
universal: {
|
|
67
|
+
user_id: 'user-456',
|
|
68
|
+
},
|
|
69
|
+
targetSubscriber: 'audit-service',
|
|
70
|
+
},
|
|
71
|
+
expected: {
|
|
72
|
+
eventKey: 'profile.updated',
|
|
73
|
+
targetSubscriber: 'audit-service',
|
|
74
|
+
data: { field: 'email' },
|
|
75
|
+
metadata: { user_id: 'user-456' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'v1 message with universal.correlation_id',
|
|
80
|
+
body: {
|
|
81
|
+
key: 'request.received',
|
|
82
|
+
data: { requestId: 'req-001' },
|
|
83
|
+
universal: {
|
|
84
|
+
correlation_id: 'corr-xyz-789',
|
|
85
|
+
},
|
|
86
|
+
targetSubscriber: 'logger-service',
|
|
87
|
+
},
|
|
88
|
+
expected: {
|
|
89
|
+
eventKey: 'request.received',
|
|
90
|
+
targetSubscriber: 'logger-service',
|
|
91
|
+
data: { requestId: 'req-001' },
|
|
92
|
+
correlationId: 'corr-xyz-789',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'v1 message with all universal fields',
|
|
97
|
+
body: {
|
|
98
|
+
key: 'checkout.completed',
|
|
99
|
+
data: { cartId: 'cart-999' },
|
|
100
|
+
universal: {
|
|
101
|
+
event_id: 'evt-full-123',
|
|
102
|
+
user_id: 'user-777',
|
|
103
|
+
correlation_id: 'corr-full-456',
|
|
104
|
+
custom_field: 'custom_value',
|
|
105
|
+
},
|
|
106
|
+
targetSubscriber: 'analytics-service',
|
|
107
|
+
},
|
|
108
|
+
expected: {
|
|
109
|
+
id: 'evt-full-123',
|
|
110
|
+
eventKey: 'checkout.completed',
|
|
111
|
+
targetSubscriber: 'analytics-service',
|
|
112
|
+
data: { cartId: 'cart-999' },
|
|
113
|
+
correlationId: 'corr-full-456',
|
|
114
|
+
metadata: {
|
|
115
|
+
user_id: 'user-777',
|
|
116
|
+
custom_field: 'custom_value',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
// === METADATA MERGING EXAMPLES ===
|
|
121
|
+
{
|
|
122
|
+
name: 'v1 message with both metadata and universal (merged)',
|
|
123
|
+
body: {
|
|
124
|
+
key: 'item.added',
|
|
125
|
+
data: { itemId: 'item-123' },
|
|
126
|
+
metadata: { category: 'electronics', priority: 'high' },
|
|
127
|
+
universal: {
|
|
128
|
+
user_id: 'user-888',
|
|
129
|
+
tenant_id: 'tenant-001',
|
|
130
|
+
},
|
|
131
|
+
targetSubscriber: 'inventory-service',
|
|
132
|
+
},
|
|
133
|
+
expected: {
|
|
134
|
+
eventKey: 'item.added',
|
|
135
|
+
targetSubscriber: 'inventory-service',
|
|
136
|
+
data: { itemId: 'item-123' },
|
|
137
|
+
metadata: {
|
|
138
|
+
category: 'electronics',
|
|
139
|
+
priority: 'high',
|
|
140
|
+
user_id: 'user-888',
|
|
141
|
+
tenant_id: 'tenant-001',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
// === HEADER-BASED EXAMPLES ===
|
|
146
|
+
{
|
|
147
|
+
name: 'v1 message with attempt count from header',
|
|
148
|
+
body: {
|
|
149
|
+
key: 'task.retry',
|
|
150
|
+
data: { taskId: 'task-555' },
|
|
151
|
+
targetSubscriber: 'worker-service',
|
|
152
|
+
},
|
|
153
|
+
headers: {
|
|
154
|
+
'x-matador-attempts': 3,
|
|
155
|
+
},
|
|
156
|
+
expected: {
|
|
157
|
+
eventKey: 'task.retry',
|
|
158
|
+
targetSubscriber: 'worker-service',
|
|
159
|
+
data: { taskId: 'task-555' },
|
|
160
|
+
attempts: 3,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'v1 message with importance from header',
|
|
165
|
+
body: {
|
|
166
|
+
key: 'alert.triggered',
|
|
167
|
+
data: { alertId: 'alert-111' },
|
|
168
|
+
targetSubscriber: 'pager-service',
|
|
169
|
+
},
|
|
170
|
+
headers: {
|
|
171
|
+
'x-matador-importance': 'must-investigate',
|
|
172
|
+
},
|
|
173
|
+
expected: {
|
|
174
|
+
eventKey: 'alert.triggered',
|
|
175
|
+
targetSubscriber: 'pager-service',
|
|
176
|
+
data: { alertId: 'alert-111' },
|
|
177
|
+
importance: 'must-investigate',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'v1 message with correlation_id from v1 header',
|
|
182
|
+
body: {
|
|
183
|
+
key: 'trace.event',
|
|
184
|
+
data: { traceId: 'trace-222' },
|
|
185
|
+
targetSubscriber: 'tracing-service',
|
|
186
|
+
},
|
|
187
|
+
headers: {
|
|
188
|
+
'x-correlation-id': 'header-corr-id',
|
|
189
|
+
},
|
|
190
|
+
expected: {
|
|
191
|
+
eventKey: 'trace.event',
|
|
192
|
+
targetSubscriber: 'tracing-service',
|
|
193
|
+
data: { traceId: 'trace-222' },
|
|
194
|
+
correlationId: 'header-corr-id',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: 'v1 message with event_id from v1 header',
|
|
199
|
+
body: {
|
|
200
|
+
key: 'legacy.event',
|
|
201
|
+
data: { legacyId: 'leg-333' },
|
|
202
|
+
targetSubscriber: 'legacy-handler',
|
|
203
|
+
},
|
|
204
|
+
headers: {
|
|
205
|
+
'x-event-id': 'header-event-id-456',
|
|
206
|
+
},
|
|
207
|
+
expected: {
|
|
208
|
+
id: 'header-event-id-456',
|
|
209
|
+
eventKey: 'legacy.event',
|
|
210
|
+
targetSubscriber: 'legacy-handler',
|
|
211
|
+
data: { legacyId: 'leg-333' },
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
// === EDGE CASES ===
|
|
215
|
+
{
|
|
216
|
+
name: 'v1 message with null universal fields (ignored)',
|
|
217
|
+
body: {
|
|
218
|
+
key: 'null.test',
|
|
219
|
+
data: { test: true },
|
|
220
|
+
universal: {
|
|
221
|
+
event_id: null,
|
|
222
|
+
user_id: null,
|
|
223
|
+
correlation_id: null,
|
|
224
|
+
},
|
|
225
|
+
targetSubscriber: 'null-handler',
|
|
226
|
+
},
|
|
227
|
+
expected: {
|
|
228
|
+
eventKey: 'null.test',
|
|
229
|
+
targetSubscriber: 'null-handler',
|
|
230
|
+
data: { test: true },
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'v1 message with empty metadata object',
|
|
235
|
+
body: {
|
|
236
|
+
key: 'empty.metadata',
|
|
237
|
+
data: { value: 42 },
|
|
238
|
+
metadata: {},
|
|
239
|
+
targetSubscriber: 'empty-handler',
|
|
240
|
+
},
|
|
241
|
+
expected: {
|
|
242
|
+
eventKey: 'empty.metadata',
|
|
243
|
+
targetSubscriber: 'empty-handler',
|
|
244
|
+
data: { value: 42 },
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'v1 message with complex nested data',
|
|
249
|
+
body: {
|
|
250
|
+
key: 'complex.event',
|
|
251
|
+
data: {
|
|
252
|
+
nested: {
|
|
253
|
+
deeply: {
|
|
254
|
+
value: [1, 2, { three: 'four' }],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
array: ['a', 'b', 'c'],
|
|
258
|
+
},
|
|
259
|
+
targetSubscriber: 'complex-handler',
|
|
260
|
+
},
|
|
261
|
+
expected: {
|
|
262
|
+
eventKey: 'complex.event',
|
|
263
|
+
targetSubscriber: 'complex-handler',
|
|
264
|
+
data: {
|
|
265
|
+
nested: {
|
|
266
|
+
deeply: {
|
|
267
|
+
value: [1, 2, { three: 'four' }],
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
array: ['a', 'b', 'c'],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: 'v1 message with before field (ignored in v2)',
|
|
276
|
+
body: {
|
|
277
|
+
key: 'update.event',
|
|
278
|
+
data: { newValue: 'after' },
|
|
279
|
+
before: { oldValue: 'before' },
|
|
280
|
+
targetSubscriber: 'update-handler',
|
|
281
|
+
},
|
|
282
|
+
expected: {
|
|
283
|
+
eventKey: 'update.event',
|
|
284
|
+
targetSubscriber: 'update-handler',
|
|
285
|
+
data: { newValue: 'after' },
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
// === ADD YOUR EXAMPLES BELOW ===
|
|
289
|
+
// Copy this template and fill in your v1 message data:
|
|
290
|
+
//
|
|
291
|
+
// {
|
|
292
|
+
// name: 'descriptive name for your test case',
|
|
293
|
+
// body: {
|
|
294
|
+
// key: 'your.event.key',
|
|
295
|
+
// data: { /* your event data */ },
|
|
296
|
+
// metadata: { /* optional metadata */ },
|
|
297
|
+
// universal: {
|
|
298
|
+
// event_id: 'optional-event-id',
|
|
299
|
+
// user_id: 'optional-user-id',
|
|
300
|
+
// correlation_id: 'optional-correlation-id',
|
|
301
|
+
// // any other custom fields
|
|
302
|
+
// },
|
|
303
|
+
// before: { /* optional, will be ignored */ },
|
|
304
|
+
// options: { delayMs: 1000 }, // optional
|
|
305
|
+
// targetSubscriber: 'your-subscriber-name',
|
|
306
|
+
// },
|
|
307
|
+
// headers: {
|
|
308
|
+
// // optional headers
|
|
309
|
+
// 'x-matador-attempts': 1,
|
|
310
|
+
// 'x-matador-importance': 'should-investigate',
|
|
311
|
+
// 'x-event-id': 'header-event-id',
|
|
312
|
+
// 'x-correlation-id': 'header-correlation-id',
|
|
313
|
+
// },
|
|
314
|
+
// expected: {
|
|
315
|
+
// id: 'expected-id', // optional, will be generated if not in body/headers
|
|
316
|
+
// eventKey: 'your.event.key',
|
|
317
|
+
// targetSubscriber: 'your-subscriber-name',
|
|
318
|
+
// data: { /* expected data */ },
|
|
319
|
+
// metadata: { /* expected merged metadata */ },
|
|
320
|
+
// correlationId: 'expected-correlation-id',
|
|
321
|
+
// attempts: 1,
|
|
322
|
+
// importance: 'should-investigate',
|
|
323
|
+
// },
|
|
324
|
+
// },
|
|
325
|
+
];
|
|
326
|
+
describe('RabbitMQCodec', () => {
|
|
327
|
+
const codec = new RabbitMQCodec();
|
|
328
|
+
describe('v1 to v2 translation', () => {
|
|
329
|
+
for (const fixture of V1_MESSAGE_FIXTURES) {
|
|
330
|
+
it(`should translate: ${fixture.name}`, () => {
|
|
331
|
+
const body = new TextEncoder().encode(JSON.stringify(fixture.body));
|
|
332
|
+
const headers = fixture.headers ?? {};
|
|
333
|
+
const envelope = codec.decode(body, headers);
|
|
334
|
+
// Check required fields
|
|
335
|
+
expect(envelope.docket.eventKey).toBe(fixture.expected.eventKey);
|
|
336
|
+
expect(envelope.docket.targetSubscriber).toBe(fixture.expected.targetSubscriber);
|
|
337
|
+
expect(envelope.data).toEqual(fixture.expected.data);
|
|
338
|
+
// Check optional ID (may be generated if not specified)
|
|
339
|
+
if (fixture.expected.id !== undefined) {
|
|
340
|
+
expect(envelope.id).toBe(fixture.expected.id);
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
expect(envelope.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
344
|
+
}
|
|
345
|
+
// Check metadata
|
|
346
|
+
if (fixture.expected.metadata !== undefined) {
|
|
347
|
+
expect(envelope.docket.metadata).toEqual(fixture.expected.metadata);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
expect(envelope.docket.metadata === undefined ||
|
|
351
|
+
Object.keys(envelope.docket.metadata).length === 0).toBe(true);
|
|
352
|
+
}
|
|
353
|
+
// Check correlation ID
|
|
354
|
+
if (fixture.expected.correlationId !== undefined) {
|
|
355
|
+
expect(envelope.docket.correlationId).toBe(fixture.expected.correlationId);
|
|
356
|
+
}
|
|
357
|
+
// Check attempts (defaults to 1)
|
|
358
|
+
if (fixture.expected.attempts !== undefined) {
|
|
359
|
+
expect(envelope.docket.attempts).toBe(fixture.expected.attempts);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
expect(envelope.docket.attempts).toBe(1);
|
|
363
|
+
}
|
|
364
|
+
// Check importance (defaults to 'should-investigate')
|
|
365
|
+
if (fixture.expected.importance !== undefined) {
|
|
366
|
+
expect(envelope.docket.importance).toBe(fixture.expected.importance);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
expect(envelope.docket.importance).toBe('should-investigate');
|
|
370
|
+
}
|
|
371
|
+
// Check createdAt is a valid ISO string
|
|
372
|
+
expect(envelope.docket.createdAt).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
describe('v1 edge cases', () => {
|
|
377
|
+
it('should handle options.delayMs by setting scheduledFor', () => {
|
|
378
|
+
const body = {
|
|
379
|
+
key: 'delayed.event',
|
|
380
|
+
data: { test: true },
|
|
381
|
+
options: { delayMs: 5000 },
|
|
382
|
+
targetSubscriber: 'delay-handler',
|
|
383
|
+
};
|
|
384
|
+
const encoded = new TextEncoder().encode(JSON.stringify(body));
|
|
385
|
+
const before = Date.now();
|
|
386
|
+
const envelope = codec.decode(encoded, {});
|
|
387
|
+
const after = Date.now();
|
|
388
|
+
expect(envelope.docket.scheduledFor).toBeDefined();
|
|
389
|
+
const scheduledTime = new Date(envelope.docket.scheduledFor).getTime();
|
|
390
|
+
expect(scheduledTime).toBeGreaterThanOrEqual(before + 5000);
|
|
391
|
+
expect(scheduledTime).toBeLessThanOrEqual(after + 5000);
|
|
392
|
+
});
|
|
393
|
+
it('should prefer header event_id over body universal.event_id', () => {
|
|
394
|
+
const body = {
|
|
395
|
+
key: 'priority.test',
|
|
396
|
+
data: {},
|
|
397
|
+
universal: { event_id: 'body-event-id' },
|
|
398
|
+
targetSubscriber: 'test-handler',
|
|
399
|
+
};
|
|
400
|
+
const encoded = new TextEncoder().encode(JSON.stringify(body));
|
|
401
|
+
const envelope = codec.decode(encoded, {
|
|
402
|
+
'x-event-id': 'header-event-id',
|
|
403
|
+
});
|
|
404
|
+
expect(envelope.id).toBe('header-event-id');
|
|
405
|
+
});
|
|
406
|
+
it('should prefer v2 correlation header over v1 header', () => {
|
|
407
|
+
const body = {
|
|
408
|
+
key: 'corr.priority',
|
|
409
|
+
data: {},
|
|
410
|
+
targetSubscriber: 'test-handler',
|
|
411
|
+
};
|
|
412
|
+
const encoded = new TextEncoder().encode(JSON.stringify(body));
|
|
413
|
+
const envelope = codec.decode(encoded, {
|
|
414
|
+
'x-matador-correlation-id': 'v2-corr-id',
|
|
415
|
+
'x-correlation-id': 'v1-corr-id',
|
|
416
|
+
});
|
|
417
|
+
expect(envelope.docket.correlationId).toBe('v2-corr-id');
|
|
418
|
+
});
|
|
419
|
+
it('should prefer header correlation_id over body universal.correlation_id', () => {
|
|
420
|
+
const body = {
|
|
421
|
+
key: 'corr.priority',
|
|
422
|
+
data: {},
|
|
423
|
+
universal: { correlation_id: 'body-corr-id' },
|
|
424
|
+
targetSubscriber: 'test-handler',
|
|
425
|
+
};
|
|
426
|
+
const encoded = new TextEncoder().encode(JSON.stringify(body));
|
|
427
|
+
const envelope = codec.decode(encoded, {
|
|
428
|
+
'x-correlation-id': 'header-corr-id',
|
|
429
|
+
});
|
|
430
|
+
expect(envelope.docket.correlationId).toBe('header-corr-id');
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
});
|