@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.
- package/CHANGELOG.md +18 -0
- package/package.json +9 -9
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +4 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res.mjs +1 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +4 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs +1 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +4 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res.mjs +1 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs +3 -3
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +4 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs +1 -1
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +4 -1
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res.mjs +1 -1
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +4 -1
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res.mjs +1 -1
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res.mjs +2 -2
- package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res.mjs +1 -1
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +4 -1
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +1 -1
- package/src/adapter/Postgres/PgProjectionFeed.res +4 -1
- package/src/adapter/Postgres/PgProjectionFeed.res.mjs +1 -1
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +12 -0
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +4 -1
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res.mjs +14 -14
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +4 -4
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +5 -5
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +3 -3
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +2 -2
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +1 -1
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +18 -1
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +20 -6
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_EventBridgeScheduler_Runtime.res.mjs +1 -1
- package/src/plugin/runtime/Util_PluginMessage_Runtime.res.mjs +1 -1
- package/src/util/Util_CognitoGroupUser_Runtime.res.mjs +2 -2
- package/src/util/Util_Cognito_Runtime.res.mjs +1 -1
- package/src/util/Util_DeadLetterHandler.res +138 -0
- package/src/util/Util_DeadLetterHandler.res.mjs +113 -0
- package/src/util/Util_DeadLetterQueue.res +10 -53
- package/src/util/Util_DeadLetterQueue.res.mjs +6 -26
- package/src/util/Util_DynamoDb_Runtime.res.mjs +4 -4
- package/src/util/Util_Kinesis_Runtime.res.mjs +1 -1
- package/src/util/Util_SNS_Runtime.res.mjs +2 -2
- package/src/util/Util_SQS_Runtime.res.mjs +5 -5
- package/tests/Util_DeadLetterHandlerTest.res +78 -0
- 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 */
|