@reventlessdev/reventless-aws 3.0.0-alpha.174 → 3.0.0-alpha.175
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/CHANGELOG.md +8 -0
- package/package.json +6 -6
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +24 -7
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +7 -2
- package/tests/PgChangeFeedRelay_IntegrationTest.res +148 -0
- package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +154 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.175 (2026-07-05)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @reventlessdev/reventless-aws
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
# 3.0.0-alpha.174 (2026-07-05)
|
|
7
15
|
|
|
8
16
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.175",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
"uuid": "^13.0.0",
|
|
11
11
|
"@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.20",
|
|
12
12
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.24",
|
|
13
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.5",
|
|
14
13
|
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.45",
|
|
14
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.5",
|
|
15
15
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
|
|
16
16
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.13",
|
|
17
|
-
"@reventlessdev/reventless-
|
|
18
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.141",
|
|
17
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.142",
|
|
19
18
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.23",
|
|
20
|
-
"@reventlessdev/reventless-
|
|
21
|
-
"@reventlessdev/reventless-
|
|
19
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.88",
|
|
20
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.4",
|
|
21
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.66"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"rescript": "^12.3.0",
|
|
@@ -38,17 +38,19 @@ let toEventCollectorJson = (
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Drain a Postgres DCB log from its checkpoint and relay its
|
|
42
|
-
* EventCollector via the injected `sendBatch`. `
|
|
43
|
-
*
|
|
44
|
-
*
|
|
41
|
+
* Drain a Postgres DCB log from its checkpoint on the given `pool` and relay its
|
|
42
|
+
* events to the EventCollector via the injected `sendBatch`. Both `pool` and
|
|
43
|
+
* `sendBatch` are injected (rather than hardcoding Secrets Manager / SQS) so the
|
|
44
|
+
* whole drain→transform→send path is integration-testable against a plain local
|
|
45
|
+
* Postgres (B2.4); the `relay` wrapper below builds the container pool for the
|
|
46
|
+
* deployed Lambda. Returns the number of events processed.
|
|
45
47
|
*
|
|
46
48
|
* At-least-once by construction: `PgChangeFeed.drain` replays the last page on a
|
|
47
49
|
* crash before checkpoint, and EventCollector projections are idempotent
|
|
48
50
|
* (event-sourced), so re-delivery is safe.
|
|
49
51
|
*/
|
|
50
|
-
let
|
|
51
|
-
~
|
|
52
|
+
let relayWithPool = async (
|
|
53
|
+
~pool: ReventlessPostgres.PgDriver.pool,
|
|
52
54
|
~logName: string,
|
|
53
55
|
~subscriber: string,
|
|
54
56
|
// ~partitionTagJson: the DCB log's partition tag, sury-encoded into HANDLER_CONFIG
|
|
@@ -62,7 +64,6 @@ let relay = async (
|
|
|
62
64
|
partitionTagJson->Option.map(json =>
|
|
63
65
|
json->S.parseJsonOrThrow(Reventless.DcbTag.derivedPartitionTagSchema)
|
|
64
66
|
)
|
|
65
|
-
let pool = PgRuntime.poolFor(config)
|
|
66
67
|
await ReventlessPostgres.PgChangeFeed.drain(pool, ~logName, ~subscriber, ~handle=async events => {
|
|
67
68
|
let jsons = events->Array.filterMap(event => toEventCollectorJson(event, ~partitionTag?))
|
|
68
69
|
if jsons->Array.length > 0 {
|
|
@@ -70,3 +71,19 @@ let relay = async (
|
|
|
70
71
|
}
|
|
71
72
|
})
|
|
72
73
|
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Deployed-Lambda entry: resolve the container-lifetime pool from the injected
|
|
77
|
+
* `PgConnection.connectionConfig` (via Secrets Manager) and relay. Thin wrapper over
|
|
78
|
+
* `relayWithPool`.
|
|
79
|
+
*/
|
|
80
|
+
let relay = async (
|
|
81
|
+
~config: PgConnection.connectionConfig,
|
|
82
|
+
~logName: string,
|
|
83
|
+
~subscriber: string,
|
|
84
|
+
~partitionTagJson: option<JSON.t>=?,
|
|
85
|
+
~sendBatch: array<JSON.t> => promise<unit>,
|
|
86
|
+
): int => {
|
|
87
|
+
let pool = PgRuntime.poolFor(config)
|
|
88
|
+
await relayWithPool(~pool, ~logName, ~subscriber, ~partitionTagJson?, ~sendBatch)
|
|
89
|
+
}
|
|
@@ -22,9 +22,8 @@ function toEventCollectorJson(event, partitionTag) {
|
|
|
22
22
|
return Util_DynamoDbStream_Runtime$ReventlessAws.buildJsonEvent$p(item);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
async function
|
|
25
|
+
async function relayWithPool(pool, logName, subscriber, partitionTagJson, sendBatch) {
|
|
26
26
|
let partitionTag = Stdlib_Option.map(partitionTagJson, json => S.parseJsonOrThrow(json, DcbTag$Reventless.derivedPartitionTagSchema));
|
|
27
|
-
let pool = PgRuntime$ReventlessAws.poolFor(config);
|
|
28
27
|
return await PgChangeFeed$ReventlessPostgres.drain(pool, logName, subscriber, undefined, async events => {
|
|
29
28
|
let jsons = Stdlib_Array.filterMap(events, event => toEventCollectorJson(event, partitionTag));
|
|
30
29
|
if (jsons.length !== 0) {
|
|
@@ -33,8 +32,14 @@ async function relay(config, logName, subscriber, partitionTagJson, sendBatch) {
|
|
|
33
32
|
});
|
|
34
33
|
}
|
|
35
34
|
|
|
35
|
+
async function relay(config, logName, subscriber, partitionTagJson, sendBatch) {
|
|
36
|
+
let pool = PgRuntime$ReventlessAws.poolFor(config);
|
|
37
|
+
return await relayWithPool(pool, logName, subscriber, partitionTagJson, sendBatch);
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
export {
|
|
37
41
|
toEventCollectorJson,
|
|
42
|
+
relayWithPool,
|
|
38
43
|
relay,
|
|
39
44
|
}
|
|
40
45
|
/* S Not a pure module */
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Live-Postgres integration suite for the change-feed relay (B2.4).
|
|
2
|
+
//
|
|
3
|
+
// Skipped unless PG_URL is set, so the default `pnpm test` stays dependency-free.
|
|
4
|
+
// Run against a real database with e.g.:
|
|
5
|
+
// PG_URL=postgres://postgres:postgres@localhost:5432/postgres pnpm test
|
|
6
|
+
//
|
|
7
|
+
// Exercises the deployed propagation path end-to-end minus AWS: DCB events are
|
|
8
|
+
// appended to a real `dcb_event` log, then `PgChangeFeedRelay_Runtime.relayWithPool`
|
|
9
|
+
// drains the feed, transforms each event into the EventCollector `{id, meta, event}`
|
|
10
|
+
// body, and hands the batch to an injected `sendBatch` (standing in for the SQS
|
|
11
|
+
// SendMessage the entry point wires). Asserts the emitted bodies, the partition-tag
|
|
12
|
+
// `id` derivation, and the checkpoint (a second drain sees nothing new).
|
|
13
|
+
|
|
14
|
+
open JestGlobals
|
|
15
|
+
open ReventlessCore
|
|
16
|
+
open Reventless
|
|
17
|
+
|
|
18
|
+
@val external processEnv: dict<string> = "process.env"
|
|
19
|
+
let opts: Pulumi.CustomResourceOptions.t = {}
|
|
20
|
+
|
|
21
|
+
let jsonObj = pairs => JSON.Encode.object(Dict.fromArray(pairs))
|
|
22
|
+
|
|
23
|
+
let stored = (eventType, tags, data): DcbEventLog_Adapter.rawStoredEvent => {
|
|
24
|
+
eventType,
|
|
25
|
+
data,
|
|
26
|
+
tags,
|
|
27
|
+
meta: ReventlessCore.Message.generateMeta(~service="relay-it"),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Collect every JSON body handed to sendBatch across all drain pages.
|
|
31
|
+
let capturingSendBatch = sink => async jsons => sink := sink.contents->Array.concat(jsons)
|
|
32
|
+
|
|
33
|
+
let idOf = json =>
|
|
34
|
+
json->JSON.Decode.object->Option.flatMap(o => o->Dict.get("id"))->Option.flatMap(JSON.Decode.string)
|
|
35
|
+
|
|
36
|
+
switch processEnv->Dict.get("PG_URL") {
|
|
37
|
+
| None =>
|
|
38
|
+
testSync("Postgres relay integration (skipped — set PG_URL to run)", () =>
|
|
39
|
+
expect(true)->toBe(true)
|
|
40
|
+
)
|
|
41
|
+
| Some(url) =>
|
|
42
|
+
let pool = ReventlessPostgres.PgDriver.makePool({connectionString: url})
|
|
43
|
+
|
|
44
|
+
let makeLog = name =>
|
|
45
|
+
ReventlessPostgres.DcbEventLogStorage_Postgres.makeStorage(
|
|
46
|
+
~pool,
|
|
47
|
+
~name,
|
|
48
|
+
~indexes=[],
|
|
49
|
+
~partitionTag=DcbTag.Simple({key: "orderId"}),
|
|
50
|
+
~opts,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
beforeAllAsync(async () => {
|
|
54
|
+
await ReventlessPostgres.PgSchema.ensureSchema(pool)
|
|
55
|
+
await ReventlessPostgres.PgSchema.truncateAll(pool)
|
|
56
|
+
})
|
|
57
|
+
afterAll(() => {
|
|
58
|
+
let _ = pool->ReventlessPostgres.PgDriver.endPool
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
describe("PgChangeFeedRelay end-to-end (append → drain → EventCollector bodies)", () => {
|
|
62
|
+
testPromise("relays each appended DCB event as an {id, meta, event} body", async () => {
|
|
63
|
+
let (_n, ops, _s) = makeLog("relay-basic")
|
|
64
|
+
let mk = i =>
|
|
65
|
+
stored("OrderPlaced", [{key: "orderId", value: "o-1"}], jsonObj([("i", JSON.Encode.int(i))]))
|
|
66
|
+
let _ = await ops.append([mk(0), mk(1), mk(2)])
|
|
67
|
+
|
|
68
|
+
let sink = ref([])
|
|
69
|
+
let processed = await PgChangeFeedRelay_Runtime.relayWithPool(
|
|
70
|
+
~pool,
|
|
71
|
+
~logName="relay-basic",
|
|
72
|
+
~subscriber="relay-it",
|
|
73
|
+
~sendBatch=capturingSendBatch(sink),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
expect(processed)->toBe(3)
|
|
77
|
+
expect(sink.contents->Array.length)->toBe(3)
|
|
78
|
+
|
|
79
|
+
// Every body carries the derived id, the event type, and the producing service.
|
|
80
|
+
let first = sink.contents->Array.getUnsafe(0)
|
|
81
|
+
expect(idOf(first))->toEqual(Some("orderId:o-1"))
|
|
82
|
+
let obj = first->JSON.Decode.object->Option.getOrThrow
|
|
83
|
+
expect(obj->Dict.get("event")->Option.getOrThrow->JSON.stringify->String.includes("OrderPlaced"))->toBe(true)
|
|
84
|
+
let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
|
|
85
|
+
expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("relay-it")))
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
testPromise("checkpoints — a second drain from the same subscriber sees nothing new", async () => {
|
|
89
|
+
let (_n, ops, _s) = makeLog("relay-checkpoint")
|
|
90
|
+
let mk = i => stored("Fed", [{key: "orderId", value: "f"}], jsonObj([("i", JSON.Encode.int(i))]))
|
|
91
|
+
let _ = await ops.append([mk(0), mk(1)])
|
|
92
|
+
|
|
93
|
+
let sink = ref([])
|
|
94
|
+
let first = await PgChangeFeedRelay_Runtime.relayWithPool(
|
|
95
|
+
~pool,
|
|
96
|
+
~logName="relay-checkpoint",
|
|
97
|
+
~subscriber="relay-it",
|
|
98
|
+
~sendBatch=capturingSendBatch(sink),
|
|
99
|
+
)
|
|
100
|
+
expect(first)->toBe(2)
|
|
101
|
+
|
|
102
|
+
let second = await PgChangeFeedRelay_Runtime.relayWithPool(
|
|
103
|
+
~pool,
|
|
104
|
+
~logName="relay-checkpoint",
|
|
105
|
+
~subscriber="relay-it",
|
|
106
|
+
~sendBatch=capturingSendBatch(sink),
|
|
107
|
+
)
|
|
108
|
+
expect(second)->toBe(0)
|
|
109
|
+
// No extra bodies were emitted on the second pass.
|
|
110
|
+
expect(sink.contents->Array.length)->toBe(2)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
testPromise("partitionTag pins the id to its tag on a multi-tag event", async () => {
|
|
114
|
+
let (_n, ops, _s) =
|
|
115
|
+
ReventlessPostgres.DcbEventLogStorage_Postgres.makeStorage(
|
|
116
|
+
~pool,
|
|
117
|
+
~name="relay-pt",
|
|
118
|
+
~indexes=[],
|
|
119
|
+
~partitionTag=DcbTag.Simple({key: "customerId"}),
|
|
120
|
+
~opts,
|
|
121
|
+
)
|
|
122
|
+
let ev =
|
|
123
|
+
stored(
|
|
124
|
+
"OrderPlaced",
|
|
125
|
+
[{key: "orderId", value: "o-9"}, {key: "customerId", value: "c-9"}],
|
|
126
|
+
jsonObj([("n", JSON.Encode.int(1))]),
|
|
127
|
+
)
|
|
128
|
+
let _ = await ops.append([ev])
|
|
129
|
+
|
|
130
|
+
// The relay receives the sury-encoded partition tag exactly as the builder emits it.
|
|
131
|
+
let partitionTagJson =
|
|
132
|
+
DcbTag.Simple({key: "customerId"})->S.reverseConvertToJsonOrThrow(
|
|
133
|
+
Reventless.DcbTag.derivedPartitionTagSchema,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
let sink = ref([])
|
|
137
|
+
let _ = await PgChangeFeedRelay_Runtime.relayWithPool(
|
|
138
|
+
~pool,
|
|
139
|
+
~logName="relay-pt",
|
|
140
|
+
~subscriber="relay-it",
|
|
141
|
+
~partitionTagJson,
|
|
142
|
+
~sendBatch=capturingSendBatch(sink),
|
|
143
|
+
)
|
|
144
|
+
// Without the tag the id would be "orderId:o-9" (first tag); the tag pins it.
|
|
145
|
+
expect(idOf(sink.contents->Array.getUnsafe(0)))->toEqual(Some("customerId:c-9"))
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
7
|
+
import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
8
|
+
import * as PgDriver$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgDriver.res.mjs";
|
|
9
|
+
import * as PgSchema$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/PgSchema.res.mjs";
|
|
10
|
+
import * as PgChangeFeedRelay_Runtime$ReventlessAws from "../src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs";
|
|
11
|
+
import * as DcbEventLogStorage_Postgres$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/DcbEventLogStorage_Postgres.res.mjs";
|
|
12
|
+
|
|
13
|
+
let opts = {};
|
|
14
|
+
|
|
15
|
+
function jsonObj(pairs) {
|
|
16
|
+
return Object.fromEntries(pairs);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function stored(eventType, tags, data) {
|
|
20
|
+
return {
|
|
21
|
+
eventType: eventType,
|
|
22
|
+
data: data,
|
|
23
|
+
tags: tags,
|
|
24
|
+
meta: Message$ReventlessCore.generateMeta("relay-it", undefined, undefined, undefined, undefined, undefined, undefined, undefined)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function capturingSendBatch(sink) {
|
|
29
|
+
return async jsons => {
|
|
30
|
+
sink.contents = sink.contents.concat(jsons);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function idOf(json) {
|
|
35
|
+
return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), o => o["id"]), Stdlib_JSON.Decode.string);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let url = process.env["PG_URL"];
|
|
39
|
+
|
|
40
|
+
if (url !== undefined) {
|
|
41
|
+
let pool = PgDriver$ReventlessPostgres.makePool({
|
|
42
|
+
connectionString: url
|
|
43
|
+
});
|
|
44
|
+
let makeLog = name => DcbEventLogStorage_Postgres$ReventlessPostgres.makeStorage(pool, name, [], {
|
|
45
|
+
TAG: "Simple",
|
|
46
|
+
_0: {
|
|
47
|
+
key: "orderId"
|
|
48
|
+
}
|
|
49
|
+
}, undefined, opts, undefined, undefined);
|
|
50
|
+
globalThis.beforeAll(async () => {
|
|
51
|
+
await PgSchema$ReventlessPostgres.ensureSchema(pool);
|
|
52
|
+
return await PgSchema$ReventlessPostgres.truncateAll(pool);
|
|
53
|
+
});
|
|
54
|
+
globalThis.afterAll(() => {
|
|
55
|
+
PgDriver$ReventlessPostgres.endPool(pool);
|
|
56
|
+
});
|
|
57
|
+
globalThis.describe("PgChangeFeedRelay end-to-end (append → drain → EventCollector bodies)", () => {
|
|
58
|
+
globalThis.test("relays each appended DCB event as an {id, meta, event} body", async () => {
|
|
59
|
+
let match = makeLog("relay-basic");
|
|
60
|
+
let mk = i => stored("OrderPlaced", [{
|
|
61
|
+
key: "orderId",
|
|
62
|
+
value: "o-1"
|
|
63
|
+
}], Object.fromEntries([[
|
|
64
|
+
"i",
|
|
65
|
+
i
|
|
66
|
+
]]));
|
|
67
|
+
await match[1].append([
|
|
68
|
+
mk(0),
|
|
69
|
+
mk(1),
|
|
70
|
+
mk(2)
|
|
71
|
+
], undefined);
|
|
72
|
+
let sink = {
|
|
73
|
+
contents: []
|
|
74
|
+
};
|
|
75
|
+
let processed = await PgChangeFeedRelay_Runtime$ReventlessAws.relayWithPool(pool, "relay-basic", "relay-it", undefined, capturingSendBatch(sink));
|
|
76
|
+
globalThis.expect(processed).toBe(3);
|
|
77
|
+
globalThis.expect(sink.contents.length).toBe(3);
|
|
78
|
+
let first = sink.contents[0];
|
|
79
|
+
globalThis.expect(idOf(first)).toEqual("orderId:o-1");
|
|
80
|
+
let obj = Stdlib_Option.getOrThrow(Stdlib_JSON.Decode.object(first), undefined);
|
|
81
|
+
globalThis.expect(JSON.stringify(Stdlib_Option.getOrThrow(obj["event"], undefined)).includes("OrderPlaced")).toBe(true);
|
|
82
|
+
let metaObj = Stdlib_Option.getOrThrow(Stdlib_Option.flatMap(obj["meta"], Stdlib_JSON.Decode.object), undefined);
|
|
83
|
+
globalThis.expect(metaObj["service"]).toEqual("relay-it");
|
|
84
|
+
});
|
|
85
|
+
globalThis.test("checkpoints — a second drain from the same subscriber sees nothing new", async () => {
|
|
86
|
+
let match = makeLog("relay-checkpoint");
|
|
87
|
+
let mk = i => stored("Fed", [{
|
|
88
|
+
key: "orderId",
|
|
89
|
+
value: "f"
|
|
90
|
+
}], Object.fromEntries([[
|
|
91
|
+
"i",
|
|
92
|
+
i
|
|
93
|
+
]]));
|
|
94
|
+
await match[1].append([
|
|
95
|
+
mk(0),
|
|
96
|
+
mk(1)
|
|
97
|
+
], undefined);
|
|
98
|
+
let sink = {
|
|
99
|
+
contents: []
|
|
100
|
+
};
|
|
101
|
+
let first = await PgChangeFeedRelay_Runtime$ReventlessAws.relayWithPool(pool, "relay-checkpoint", "relay-it", undefined, capturingSendBatch(sink));
|
|
102
|
+
globalThis.expect(first).toBe(2);
|
|
103
|
+
let second = await PgChangeFeedRelay_Runtime$ReventlessAws.relayWithPool(pool, "relay-checkpoint", "relay-it", undefined, capturingSendBatch(sink));
|
|
104
|
+
globalThis.expect(second).toBe(0);
|
|
105
|
+
globalThis.expect(sink.contents.length).toBe(2);
|
|
106
|
+
});
|
|
107
|
+
globalThis.test("partitionTag pins the id to its tag on a multi-tag event", async () => {
|
|
108
|
+
let match = DcbEventLogStorage_Postgres$ReventlessPostgres.makeStorage(pool, "relay-pt", [], {
|
|
109
|
+
TAG: "Simple",
|
|
110
|
+
_0: {
|
|
111
|
+
key: "customerId"
|
|
112
|
+
}
|
|
113
|
+
}, undefined, opts, undefined, undefined);
|
|
114
|
+
let ev = stored("OrderPlaced", [
|
|
115
|
+
{
|
|
116
|
+
key: "orderId",
|
|
117
|
+
value: "o-9"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
key: "customerId",
|
|
121
|
+
value: "c-9"
|
|
122
|
+
}
|
|
123
|
+
], Object.fromEntries([[
|
|
124
|
+
"n",
|
|
125
|
+
1
|
|
126
|
+
]]));
|
|
127
|
+
await match[1].append([ev], undefined);
|
|
128
|
+
let partitionTagJson = S.reverseConvertToJsonOrThrow({
|
|
129
|
+
TAG: "Simple",
|
|
130
|
+
_0: {
|
|
131
|
+
key: "customerId"
|
|
132
|
+
}
|
|
133
|
+
}, DcbTag$Reventless.derivedPartitionTagSchema);
|
|
134
|
+
let sink = {
|
|
135
|
+
contents: []
|
|
136
|
+
};
|
|
137
|
+
await PgChangeFeedRelay_Runtime$ReventlessAws.relayWithPool(pool, "relay-pt", "relay-it", partitionTagJson, capturingSendBatch(sink));
|
|
138
|
+
globalThis.expect(idOf(sink.contents[0])).toEqual("customerId:c-9");
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
} else {
|
|
142
|
+
globalThis.test("Postgres relay integration (skipped — set PG_URL to run)", () => {
|
|
143
|
+
globalThis.expect(true).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export {
|
|
148
|
+
opts,
|
|
149
|
+
jsonObj,
|
|
150
|
+
stored,
|
|
151
|
+
capturingSendBatch,
|
|
152
|
+
idOf,
|
|
153
|
+
}
|
|
154
|
+
/* url Not a pure module */
|