@reventlessdev/reventless-aws 3.0.0-alpha.419 → 3.0.0-alpha.421

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 (45) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +9 -9
  3. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +4 -1
  4. package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res.mjs +1 -1
  5. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +4 -1
  6. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs +1 -1
  7. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +4 -1
  8. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res.mjs +1 -1
  9. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs +3 -3
  10. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +4 -1
  11. package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs +1 -1
  12. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +4 -1
  13. package/src/adapter/EventCollector/EventCollectorChannel_SQS.res.mjs +1 -1
  14. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +4 -1
  15. package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res.mjs +1 -1
  16. package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res.mjs +2 -2
  17. package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res.mjs +1 -1
  18. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +4 -1
  19. package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +1 -1
  20. package/src/adapter/Postgres/PgProjectionFeed.res +4 -1
  21. package/src/adapter/Postgres/PgProjectionFeed.res.mjs +1 -1
  22. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +12 -0
  23. package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +4 -1
  24. package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res.mjs +14 -14
  25. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +4 -4
  26. package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +5 -5
  27. package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +3 -3
  28. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +2 -2
  29. package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +1 -1
  30. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +18 -1
  31. package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +20 -6
  32. package/src/adapter/ScheduledPublisher/ScheduledPublisher_EventBridgeScheduler_Runtime.res.mjs +1 -1
  33. package/src/plugin/runtime/Util_PluginMessage_Runtime.res.mjs +1 -1
  34. package/src/util/Util_CognitoGroupUser_Runtime.res.mjs +2 -2
  35. package/src/util/Util_Cognito_Runtime.res.mjs +1 -1
  36. package/src/util/Util_DeadLetterHandler.res +138 -0
  37. package/src/util/Util_DeadLetterHandler.res.mjs +113 -0
  38. package/src/util/Util_DeadLetterQueue.res +10 -53
  39. package/src/util/Util_DeadLetterQueue.res.mjs +6 -26
  40. package/src/util/Util_DynamoDb_Runtime.res.mjs +4 -4
  41. package/src/util/Util_Kinesis_Runtime.res.mjs +1 -1
  42. package/src/util/Util_SNS_Runtime.res.mjs +2 -2
  43. package/src/util/Util_SQS_Runtime.res.mjs +5 -5
  44. package/tests/Util_DeadLetterHandlerTest.res +78 -0
  45. package/tests/Util_DeadLetterHandlerTest.res.mjs +101 -0
@@ -0,0 +1,78 @@
1
+ open JestGlobals
2
+
3
+ module H = Util_DeadLetterHandler
4
+
5
+ // A dead letter writes one full line on its first delivery here and a short one on every
6
+ // redelivery. A message moved here by a redrive policy keeps its receive count, so its first
7
+ // delivery reports `maxReceiveCount + 1`; reading only 1 as first meant the full line was
8
+ // never written for any of them.
9
+
10
+ let recordWith = (~receiveCount: int): H.record => {
11
+ messageId: "m-1",
12
+ body: `{"command":{"TAG":"Heartbeat"}}`,
13
+ attributes: Dict.fromArray([
14
+ ("ApproximateReceiveCount", Int.toString(receiveCount)),
15
+ ("DeadLetterQueueSourceArn", "arn:aws:sqs:eu-west-1:1:CmdTopic"),
16
+ ]),
17
+ }
18
+
19
+ let isItem = line => line->String.startsWith("DEAD LETTER ITEM:")
20
+
21
+ describe("the dead-letter handler's line", () => {
22
+ testSync("is the full one when a redriven message first arrives", () => {
23
+ let line = H.lineOf(recordWith(~receiveCount=H.maxReceiveCount + 1))
24
+ expect((isItem(line), line->String.includes("kind=Heartbeat")))->toEqual((true, true))
25
+ })
26
+
27
+ testSync("is the full one for a message sent here directly", () =>
28
+ expect(isItem(H.lineOf(recordWith(~receiveCount=1))))->toBe(true)
29
+ )
30
+
31
+ testSync("is the short one on a redelivery", () => {
32
+ let line = H.lineOf(recordWith(~receiveCount=H.maxReceiveCount + 2))
33
+ expect((
34
+ line->String.startsWith("DEAD LETTER REDELIVERY:"),
35
+ line->String.includes("kind="),
36
+ ))->toEqual((true, false))
37
+ })
38
+
39
+ testSync("names an unreadable body's kind unknown, and never writes the body", () => {
40
+ let line = H.lineOf({
41
+ messageId: "m-2",
42
+ body: "not json, about a person",
43
+ attributes: Dict.make(),
44
+ })
45
+ expect((
46
+ line->String.includes("kind=unknown"),
47
+ line->String.includes("about a person"),
48
+ ))->toEqual((true, false))
49
+ })
50
+ })
51
+
52
+ describe("the dead-letter handler", () => {
53
+ test("fails on purpose, so the messages stay on the queue", async () => {
54
+ let failed = switch await H.handler({records: [recordWith(~receiveCount=6)]}) {
55
+ | () => false
56
+ | exception _ => true
57
+ }
58
+ expect(failed)->toBe(true)
59
+ })
60
+
61
+ // The function ships the compiled file alone, so it must import nothing.
62
+ testSync("compiles to a file that imports nothing", () => {
63
+ let text = NodeFs.readFileSync(
64
+ NodePath.join([
65
+ NodePath.dirname(NodeUrl.fileURLToPath(%raw("import.meta.url"))),
66
+ "..",
67
+ "src",
68
+ "util",
69
+ "Util_DeadLetterHandler.res.mjs",
70
+ ]),
71
+ )
72
+ expect(
73
+ text
74
+ ->String.split("\n")
75
+ ->Array.some(l => l->String.startsWith("import ") || l->String.includes("require(")),
76
+ )->toBe(false)
77
+ })
78
+ })
@@ -0,0 +1,101 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Nodefs from "node:fs";
4
+ import * as Nodeurl from "node:url";
5
+ import * as Nodepath from "node:path";
6
+ import * as Util_DeadLetterHandler$ReventlessAws from "../src/util/Util_DeadLetterHandler.res.mjs";
7
+
8
+ function recordWith(receiveCount) {
9
+ return {
10
+ messageId: "m-1",
11
+ body: `{"command":{"TAG":"Heartbeat"}}`,
12
+ attributes: Object.fromEntries([
13
+ [
14
+ "ApproximateReceiveCount",
15
+ receiveCount.toString()
16
+ ],
17
+ [
18
+ "DeadLetterQueueSourceArn",
19
+ "arn:aws:sqs:eu-west-1:1:CmdTopic"
20
+ ]
21
+ ])
22
+ };
23
+ }
24
+
25
+ function isItem(line) {
26
+ return line.startsWith("DEAD LETTER ITEM:");
27
+ }
28
+
29
+ globalThis.describe("the dead-letter handler's line", () => {
30
+ globalThis.test("is the full one when a redriven message first arrives", () => {
31
+ let line = Util_DeadLetterHandler$ReventlessAws.lineOf(recordWith(Util_DeadLetterHandler$ReventlessAws.maxReceiveCount + 1 | 0));
32
+ globalThis.expect([
33
+ line.startsWith("DEAD LETTER ITEM:"),
34
+ line.includes("kind=Heartbeat")
35
+ ]).toEqual([
36
+ true,
37
+ true
38
+ ]);
39
+ });
40
+ globalThis.test("is the full one for a message sent here directly", () => {
41
+ globalThis.expect(Util_DeadLetterHandler$ReventlessAws.lineOf(recordWith(1)).startsWith("DEAD LETTER ITEM:")).toBe(true);
42
+ });
43
+ globalThis.test("is the short one on a redelivery", () => {
44
+ let line = Util_DeadLetterHandler$ReventlessAws.lineOf(recordWith(Util_DeadLetterHandler$ReventlessAws.maxReceiveCount + 2 | 0));
45
+ globalThis.expect([
46
+ line.startsWith("DEAD LETTER REDELIVERY:"),
47
+ line.includes("kind=")
48
+ ]).toEqual([
49
+ true,
50
+ false
51
+ ]);
52
+ });
53
+ globalThis.test("names an unreadable body's kind unknown, and never writes the body", () => {
54
+ let line = Util_DeadLetterHandler$ReventlessAws.lineOf({
55
+ messageId: "m-2",
56
+ body: "not json, about a person",
57
+ attributes: {}
58
+ });
59
+ globalThis.expect([
60
+ line.includes("kind=unknown"),
61
+ line.includes("about a person")
62
+ ]).toEqual([
63
+ true,
64
+ false
65
+ ]);
66
+ });
67
+ });
68
+
69
+ globalThis.describe("the dead-letter handler", () => {
70
+ globalThis.test("fails on purpose, so the messages stay on the queue", async () => {
71
+ let failed;
72
+ try {
73
+ await Util_DeadLetterHandler$ReventlessAws.handler({
74
+ Records: [recordWith(6)]
75
+ });
76
+ failed = false;
77
+ } catch (exn) {
78
+ failed = true;
79
+ }
80
+ globalThis.expect(failed).toBe(true);
81
+ });
82
+ globalThis.test("compiles to a file that imports nothing", () => {
83
+ let text = Nodefs.readFileSync(Nodepath.join(Nodepath.dirname(Nodeurl.fileURLToPath(import.meta.url)), "..", "src", "util", "Util_DeadLetterHandler.res.mjs"), "utf8");
84
+ globalThis.expect(text.split("\n").some(l => {
85
+ if (l.startsWith("import ")) {
86
+ return true;
87
+ } else {
88
+ return l.includes("require(");
89
+ }
90
+ })).toBe(false);
91
+ });
92
+ });
93
+
94
+ let H;
95
+
96
+ export {
97
+ H,
98
+ recordWith,
99
+ isItem,
100
+ }
101
+ /* Not a pure module */