@reventlessdev/reventless-aws 3.0.0-alpha.232 → 3.0.0-alpha.234

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.
Files changed (110) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/package.json +7 -7
  3. package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
  4. package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
  5. package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
  6. package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
  7. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
  8. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
  9. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
  10. package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
  11. package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
  12. package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
  13. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
  14. package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
  15. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
  16. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
  17. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
  18. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
  20. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
  21. package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
  22. package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
  23. package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
  24. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
  25. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
  26. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
  27. package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
  28. package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
  29. package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
  30. package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
  31. package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +14 -1
  32. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +22 -2
  33. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +15 -4
  34. package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
  35. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
  36. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
  37. package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
  38. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
  39. package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
  40. package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
  41. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
  42. package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
  43. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
  44. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
  45. package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
  46. package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
  47. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
  48. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
  49. package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
  50. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
  51. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
  52. package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
  53. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
  54. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
  55. package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
  56. package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +26 -6
  57. package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
  58. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
  59. package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
  60. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +5 -2
  61. package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +1 -1
  62. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
  63. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
  64. package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
  65. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
  66. package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
  67. package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
  68. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
  69. package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
  70. package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
  71. package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
  72. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
  73. package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
  74. package/src/components/AutomationSlice_Builder.res +2 -0
  75. package/src/components/AutomationSlice_Builder.res.mjs +1 -1
  76. package/src/components/OutboundTranslationSlice_Builder.res +1 -0
  77. package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
  78. package/src/plugin/runtime/PluginRuntime_Builder.res +13 -4
  79. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +1 -1
  80. package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
  81. package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
  82. package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
  83. package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
  84. package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
  85. package/tests/CounterEntryPoint_OpsTest.res +115 -0
  86. package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
  87. package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
  88. package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
  89. package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
  90. package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
  91. package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
  92. package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
  93. package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
  94. package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
  95. package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
  96. package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
  97. package/tests/Platform_AdminScan_OpsTest.res +80 -0
  98. package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
  99. package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
  100. package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
  101. package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
  102. package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
  103. package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
  104. package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
  105. package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
  106. package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
  107. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
  108. package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
  109. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
  110. package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
@@ -0,0 +1,280 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as S from "sury/src/S.res.mjs";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Stream from "effect/Stream";
7
+ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
+ import * as AutomationSliceEntryPoint_Ops$ReventlessAws from "../src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs";
9
+
10
+ function str(prim) {
11
+ return prim;
12
+ }
13
+
14
+ function obj(pairs) {
15
+ return Object.fromEntries(pairs);
16
+ }
17
+
18
+ globalThis.describe("AutomationSliceEntryPoint_Ops.parseHandlerConfig", () => {
19
+ globalThis.test("empty raw config yields no handlers", () => {
20
+ globalThis.expect(AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfig("").length).toBe(0);
21
+ });
22
+ globalThis.test("decodes a full entry incl. bodyModule and context", () => {
23
+ let config = JSON.stringify(Object.fromEntries([[
24
+ "handlers",
25
+ [Object.fromEntries([
26
+ [
27
+ "specModule",
28
+ "@x/plugin/src/Order/AutomationSlice/Restock.res.mjs"
29
+ ],
30
+ [
31
+ "bodyModule",
32
+ "@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs"
33
+ ],
34
+ [
35
+ "callbackType",
36
+ "automation"
37
+ ],
38
+ [
39
+ "queryDbTableName",
40
+ "RestockTodo-abc"
41
+ ],
42
+ [
43
+ "dcbQueueUrl",
44
+ "https://sqs/queue.fifo"
45
+ ],
46
+ [
47
+ "sourceUrn",
48
+ "arn:aws:dynamodb:eu-west-1:1:table/log/stream/x"
49
+ ],
50
+ [
51
+ "context",
52
+ Object.fromEntries([
53
+ [
54
+ "environment",
55
+ "alpha"
56
+ ],
57
+ [
58
+ "platformName",
59
+ "OnlineShop"
60
+ ],
61
+ [
62
+ "pluginName",
63
+ "Ordering"
64
+ ],
65
+ [
66
+ "sliceName",
67
+ "Restock"
68
+ ]
69
+ ])
70
+ ]
71
+ ])]
72
+ ]]));
73
+ let entries = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfig(config);
74
+ globalThis.expect(entries.length).toBe(1);
75
+ let e = entries[0];
76
+ globalThis.expect(e.bodyModule).toBe("@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs");
77
+ globalThis.expect(e.callbackType).toBe("automation");
78
+ globalThis.expect(e.dcbQueueUrl).toBe("https://sqs/queue.fifo");
79
+ let ctx = e.context;
80
+ if (ctx !== undefined) {
81
+ globalThis.expect(ctx.environment).toBe("alpha");
82
+ globalThis.expect(ctx.platformName).toBe("OnlineShop");
83
+ globalThis.expect(ctx.pluginName).toBe("Ordering");
84
+ globalThis.expect(ctx.sliceName).toBe("Restock");
85
+ return;
86
+ } else {
87
+ return Stdlib_JsError.throwWithMessage("expected Some(context)");
88
+ }
89
+ });
90
+ globalThis.test("a stale entry without bodyModule/context decodes with defaults", () => {
91
+ let config = JSON.stringify(Object.fromEntries([[
92
+ "handlers",
93
+ [Object.fromEntries([
94
+ [
95
+ "specModule",
96
+ "a.res.mjs"
97
+ ],
98
+ [
99
+ "callbackType",
100
+ "outbound"
101
+ ],
102
+ [
103
+ "queryDbTableName",
104
+ "t"
105
+ ],
106
+ [
107
+ "dcbQueueUrl",
108
+ "u"
109
+ ],
110
+ [
111
+ "sourceUrn",
112
+ "arn"
113
+ ]
114
+ ])]
115
+ ]]));
116
+ let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfig(config)[0];
117
+ globalThis.expect(e.bodyModule).toBe("");
118
+ globalThis.expect(Stdlib_Option.isNone(e.context)).toBe(true);
119
+ });
120
+ });
121
+
122
+ async function noopPublish(param) {
123
+
124
+ }
125
+
126
+ globalThis.describe("AutomationSliceEntryPoint_Ops.makeAutomationJsonEventsHandler", () => {
127
+ globalThis.test("passes raw envelope JSONs + context to phase 1; sync → phase 2 → sync", async () => {
128
+ let steps = [];
129
+ let receivedJsons = {
130
+ contents: []
131
+ };
132
+ let receivedCtx = {
133
+ contents: undefined
134
+ };
135
+ let callback_todoItems = {};
136
+ let callback_phase1 = (jsons, ctx) => {
137
+ receivedJsons.contents = jsons;
138
+ receivedCtx.contents = ctx;
139
+ steps.push("phase1");
140
+ };
141
+ let callback_phase2 = async _publish => {
142
+ steps.push("phase2");
143
+ };
144
+ let callback = {
145
+ todoItems: callback_todoItems,
146
+ phase1: callback_phase1,
147
+ phase2: callback_phase2
148
+ };
149
+ let handler = AutomationSliceEntryPoint_Ops$ReventlessAws.makeAutomationJsonEventsHandler({
150
+ environment: "test",
151
+ platformName: "P",
152
+ pluginName: "Pl",
153
+ sliceName: "S"
154
+ }, callback, noopPublish, async () => {
155
+ steps.push("sync");
156
+ });
157
+ let envelope = Object.fromEntries([
158
+ [
159
+ "meta",
160
+ Object.fromEntries([[
161
+ "service",
162
+ "Order"
163
+ ]])
164
+ ],
165
+ [
166
+ "event",
167
+ "Placed"
168
+ ]
169
+ ]);
170
+ await Effect.runPromise(handler(Stream.fromIterable([
171
+ envelope,
172
+ "bare"
173
+ ])));
174
+ globalThis.expect(receivedJsons.contents).toEqual([
175
+ envelope,
176
+ "bare"
177
+ ]);
178
+ globalThis.expect(Stdlib_Option.map(receivedCtx.contents, c => c.sliceName)).toEqual("S");
179
+ globalThis.expect(steps).toEqual([
180
+ "phase1",
181
+ "sync",
182
+ "phase2",
183
+ "sync"
184
+ ]);
185
+ });
186
+ });
187
+
188
+ let outboundEventSchema = S.union([
189
+ S.schema(s => ({
190
+ TAG: "OrderPlaced",
191
+ orderId: s.m(S.string)
192
+ })),
193
+ S.literal("OrderArchived")
194
+ ]);
195
+
196
+ globalThis.describe("AutomationSliceEntryPoint_Ops.makeOutboundJsonEventsHandler", () => {
197
+ globalThis.test("decodes inner event payloads, drops non-consumed types silently", async () => {
198
+ let steps = [];
199
+ let received = {
200
+ contents: []
201
+ };
202
+ let callback_todoItems = {};
203
+ let callback_phase1 = events => {
204
+ received.contents = events;
205
+ steps.push("phase1");
206
+ };
207
+ let callback_phase2 = async _publish => {
208
+ steps.push("phase2");
209
+ };
210
+ let callback = {
211
+ todoItems: callback_todoItems,
212
+ phase1: callback_phase1,
213
+ phase2: callback_phase2
214
+ };
215
+ let handler = AutomationSliceEntryPoint_Ops$ReventlessAws.makeOutboundJsonEventsHandler(outboundEventSchema, callback, noopPublish, async () => {
216
+ steps.push("sync");
217
+ });
218
+ let placed = Object.fromEntries([
219
+ [
220
+ "meta",
221
+ Object.fromEntries([[
222
+ "service",
223
+ "Order"
224
+ ]])
225
+ ],
226
+ [
227
+ "event",
228
+ Object.fromEntries([
229
+ [
230
+ "TAG",
231
+ "OrderPlaced"
232
+ ],
233
+ [
234
+ "orderId",
235
+ "ord-1"
236
+ ]
237
+ ])
238
+ ]
239
+ ]);
240
+ let ignored = Object.fromEntries([[
241
+ "event",
242
+ Object.fromEntries([
243
+ [
244
+ "TAG",
245
+ "OrderShipped"
246
+ ],
247
+ [
248
+ "orderId",
249
+ "ord-1"
250
+ ]
251
+ ])
252
+ ]]);
253
+ await Effect.runPromise(handler(Stream.fromIterable([
254
+ placed,
255
+ ignored,
256
+ "OrderArchived"
257
+ ])));
258
+ globalThis.expect(received.contents).toEqual([
259
+ {
260
+ TAG: "OrderPlaced",
261
+ orderId: "ord-1"
262
+ },
263
+ "OrderArchived"
264
+ ]);
265
+ globalThis.expect(steps).toEqual([
266
+ "phase1",
267
+ "sync",
268
+ "phase2",
269
+ "sync"
270
+ ]);
271
+ });
272
+ });
273
+
274
+ export {
275
+ str,
276
+ obj,
277
+ noopPublish,
278
+ outboundEventSchema,
279
+ }
280
+ /* Not a pure module */
@@ -0,0 +1,115 @@
1
+ // Guards the typed cold-start core hoisted out of CounterEntryPoint.mjs:
2
+ // - parseHandlerConfig — reads `publishChannelId`, the field the deploy side
3
+ // (CounterHandler_DynamoDbStream.res) actually writes; the former shell
4
+ // read `publishQueueUrl` and published CountFinished events to an
5
+ // undefined queue URL.
6
+ // - splitRecords — the references/counts stream routing: NewImage rows carry
7
+ // increments (unparsable inc falls back to 1), NewAndOldImage on the
8
+ // references stream is a duplicate to ignore, counts take the new image on
9
+ // insert AND update, and foreign records are dropped.
10
+
11
+ open JestGlobals
12
+
13
+ let attrS = (s: string): AwsSdk.DynamoDb.Util.attributeValue => {string: s}
14
+ let attrN = (n: string): AwsSdk.DynamoDb.Util.attributeValue => {number: n}
15
+
16
+ let refsArn = "arn:stream-references"
17
+ let countsArn = "arn:stream-counts"
18
+
19
+ let mkRecord = (
20
+ ~arn: string,
21
+ ~source="aws:dynamodb",
22
+ ~keysId: string,
23
+ ~newImage: option<dict<AwsSdk.DynamoDb.Util.attributeValue>>=?,
24
+ ~oldImage: option<dict<AwsSdk.DynamoDb.Util.attributeValue>>=?,
25
+ ): PulumiAws.DynamoDb.Stream.record => {
26
+ awsRegion: "eu-west-1",
27
+ dynamodb: {
28
+ keys: {id: attrS(keysId)},
29
+ newImage: ?newImage,
30
+ oldImage: ?oldImage,
31
+ },
32
+ eventID: "e1",
33
+ eventName: INSERT,
34
+ eventSource: source,
35
+ eventSourceARN: arn,
36
+ eventVersion: "1",
37
+ userIdentity: "",
38
+ }
39
+
40
+ describe("CounterEntryPoint_Ops.parseHandlerConfig", () => {
41
+ testSync("reads publishChannelId (the field the deploy side writes)", () => {
42
+ let config = CounterEntryPoint_Ops.parseHandlerConfig(
43
+ `{"targetSpecModule":"@x/p/src/Aggregate/Product.res.mjs","countsTableName":"counts","publishChannelId":"https://sqs/q","referencesStreamArn":"${refsArn}","countsStreamArn":"${countsArn}"}`,
44
+ )
45
+ expect(config.publishChannelId)->toEqual(Some("https://sqs/q"))
46
+ expect(config.countsTableName)->toEqual(Some("counts"))
47
+ expect(config.referencesStreamArn)->toEqual(Some(refsArn))
48
+ })
49
+ })
50
+
51
+ describe("CounterEntryPoint_Ops.splitRecords", () => {
52
+ testSync("routes references and counts records by source ARN", () => {
53
+ let records = [
54
+ // references insert: {id, inc} view row → increment 2
55
+ mkRecord(
56
+ ~arn=refsArn,
57
+ ~keysId="c1#ref-a",
58
+ ~newImage=Dict.fromArray([("id", attrS("c1#ref-a")), ("inc", attrN("2"))]),
59
+ ),
60
+ // references insert without a parsable inc → fallback 1
61
+ mkRecord(
62
+ ~arn=refsArn,
63
+ ~keysId="c1#ref-b",
64
+ ~newImage=Dict.fromArray([("id", attrS("c1#ref-b"))]),
65
+ ),
66
+ // references update (NewAndOldImage) → duplicate, ignored
67
+ mkRecord(
68
+ ~arn=refsArn,
69
+ ~keysId="c1#ref-a",
70
+ ~newImage=Dict.fromArray([("id", attrS("c1#ref-a")), ("inc", attrN("1"))]),
71
+ ~oldImage=Dict.fromArray([("id", attrS("c1#ref-a")), ("inc", attrN("1"))]),
72
+ ),
73
+ // counts insert AND update both take the new image
74
+ mkRecord(
75
+ ~arn=countsArn,
76
+ ~keysId="c1",
77
+ ~newImage=Dict.fromArray([("id", attrS("c1")), ("count", attrN("0"))]),
78
+ ),
79
+ mkRecord(
80
+ ~arn=countsArn,
81
+ ~keysId="c2",
82
+ ~newImage=Dict.fromArray([("id", attrS("c2")), ("count", attrN("3"))]),
83
+ ~oldImage=Dict.fromArray([("id", attrS("c2")), ("count", attrN("4"))]),
84
+ ),
85
+ // foreign stream / foreign source → dropped
86
+ mkRecord(
87
+ ~arn="arn:other",
88
+ ~keysId="x",
89
+ ~newImage=Dict.fromArray([("id", attrS("x"))]),
90
+ ),
91
+ mkRecord(
92
+ ~arn=refsArn,
93
+ ~source="aws:sqs",
94
+ ~keysId="y",
95
+ ~newImage=Dict.fromArray([("id", attrS("y"))]),
96
+ ),
97
+ ]
98
+
99
+ let (references, counts) = CounterEntryPoint_Ops.splitRecords(
100
+ ~referencesStreamArn=refsArn,
101
+ ~countsStreamArn=countsArn,
102
+ records,
103
+ )
104
+
105
+ expect(references)->toEqual([("c1#ref-a", 2), ("c1#ref-b", 1)])
106
+ expect(counts->Array.length)->toBe(2)
107
+ expect(
108
+ counts
109
+ ->Array.getUnsafe(1)
110
+ ->JSON.Decode.object
111
+ ->Option.flatMap(o => o->Dict.get("count"))
112
+ ->Option.flatMap(JSON.Decode.float),
113
+ )->toEqual(Some(3.))
114
+ })
115
+ })
@@ -0,0 +1,150 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as CounterEntryPoint_Ops$ReventlessAws from "../src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs";
6
+
7
+ function attrS(s) {
8
+ return {
9
+ S: s
10
+ };
11
+ }
12
+
13
+ function attrN(n) {
14
+ return {
15
+ N: n
16
+ };
17
+ }
18
+
19
+ let refsArn = "arn:stream-references";
20
+
21
+ let countsArn = "arn:stream-counts";
22
+
23
+ function mkRecord(arn, sourceOpt, keysId, newImage, oldImage) {
24
+ let source = sourceOpt !== undefined ? sourceOpt : "aws:dynamodb";
25
+ return {
26
+ awsRegion: "eu-west-1",
27
+ dynamodb: {
28
+ Keys: {
29
+ id: attrS(keysId)
30
+ },
31
+ NewImage: newImage,
32
+ OldImage: oldImage
33
+ },
34
+ eventID: "e1",
35
+ eventName: "INSERT",
36
+ eventSource: source,
37
+ eventSourceARN: arn,
38
+ eventVersion: "1",
39
+ userIdentity: ""
40
+ };
41
+ }
42
+
43
+ globalThis.describe("CounterEntryPoint_Ops.parseHandlerConfig", () => {
44
+ globalThis.test("reads publishChannelId (the field the deploy side writes)", () => {
45
+ let config = CounterEntryPoint_Ops$ReventlessAws.parseHandlerConfig(`{"targetSpecModule":"@x/p/src/Aggregate/Product.res.mjs","countsTableName":"counts","publishChannelId":"https://sqs/q","referencesStreamArn":"` + refsArn + `","countsStreamArn":"` + countsArn + `"}`);
46
+ globalThis.expect(config.publishChannelId).toEqual("https://sqs/q");
47
+ globalThis.expect(config.countsTableName).toEqual("counts");
48
+ globalThis.expect(config.referencesStreamArn).toEqual(refsArn);
49
+ });
50
+ });
51
+
52
+ globalThis.describe("CounterEntryPoint_Ops.splitRecords", () => {
53
+ globalThis.test("routes references and counts records by source ARN", () => {
54
+ let records = [
55
+ mkRecord(refsArn, undefined, "c1#ref-a", Object.fromEntries([
56
+ [
57
+ "id",
58
+ attrS("c1#ref-a")
59
+ ],
60
+ [
61
+ "inc",
62
+ attrN("2")
63
+ ]
64
+ ]), undefined),
65
+ mkRecord(refsArn, undefined, "c1#ref-b", Object.fromEntries([[
66
+ "id",
67
+ attrS("c1#ref-b")
68
+ ]]), undefined),
69
+ mkRecord(refsArn, undefined, "c1#ref-a", Object.fromEntries([
70
+ [
71
+ "id",
72
+ attrS("c1#ref-a")
73
+ ],
74
+ [
75
+ "inc",
76
+ attrN("1")
77
+ ]
78
+ ]), Object.fromEntries([
79
+ [
80
+ "id",
81
+ attrS("c1#ref-a")
82
+ ],
83
+ [
84
+ "inc",
85
+ attrN("1")
86
+ ]
87
+ ])),
88
+ mkRecord(countsArn, undefined, "c1", Object.fromEntries([
89
+ [
90
+ "id",
91
+ attrS("c1")
92
+ ],
93
+ [
94
+ "count",
95
+ attrN("0")
96
+ ]
97
+ ]), undefined),
98
+ mkRecord(countsArn, undefined, "c2", Object.fromEntries([
99
+ [
100
+ "id",
101
+ attrS("c2")
102
+ ],
103
+ [
104
+ "count",
105
+ attrN("3")
106
+ ]
107
+ ]), Object.fromEntries([
108
+ [
109
+ "id",
110
+ attrS("c2")
111
+ ],
112
+ [
113
+ "count",
114
+ attrN("4")
115
+ ]
116
+ ])),
117
+ mkRecord("arn:other", undefined, "x", Object.fromEntries([[
118
+ "id",
119
+ attrS("x")
120
+ ]]), undefined),
121
+ mkRecord(refsArn, "aws:sqs", "y", Object.fromEntries([[
122
+ "id",
123
+ attrS("y")
124
+ ]]), undefined)
125
+ ];
126
+ let match = CounterEntryPoint_Ops$ReventlessAws.splitRecords(refsArn, countsArn, records);
127
+ let counts = match[1];
128
+ globalThis.expect(match[0]).toEqual([
129
+ [
130
+ "c1#ref-a",
131
+ 2
132
+ ],
133
+ [
134
+ "c1#ref-b",
135
+ 1
136
+ ]
137
+ ]);
138
+ globalThis.expect(counts.length).toBe(2);
139
+ globalThis.expect(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(counts[1]), o => o["count"]), Stdlib_JSON.Decode.float)).toEqual(3);
140
+ });
141
+ });
142
+
143
+ export {
144
+ attrS,
145
+ attrN,
146
+ refsArn,
147
+ countsArn,
148
+ mkRecord,
149
+ }
150
+ /* Not a pure module */