@reventlessdev/reventless-aws 3.0.0-alpha.188 → 3.0.0-alpha.190
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 +16 -0
- package/package.json +7 -7
- package/src/adapter/Runtime/AggregateEntryPoint.mjs +32 -35
- package/src/adapter/Runtime/AggregateEntryPoint_Ops.res +48 -0
- package/src/adapter/Runtime/AggregateEntryPoint_Ops.res.mjs +30 -0
- package/src/adapter/Runtime/CommandGeneratorEntryPoint_Ops.res +33 -0
- package/src/adapter/Runtime/CommandGeneratorEntryPoint_Ops.res.mjs +12 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +44 -130
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +221 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +104 -0
- package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +33 -0
- package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res.mjs +26 -0
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +11 -13
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +6 -12
- package/tests/integration/AggIntegrationHarness.res +53 -0
- package/tests/integration/AggIntegrationHarness.res.mjs +92 -0
- package/tests/integration/AggTestAggregate.res +21 -0
- package/tests/integration/AggTestAggregate.res.mjs +33 -0
- package/tests/integration/AggTestAggregateBehavior.res +18 -0
- package/tests/integration/AggTestAggregateBehavior.res.mjs +35 -0
- package/tests/integration/AggregateEntryPoint_IntegrationTest.res +103 -0
- package/tests/integration/AggregateEntryPoint_IntegrationTest.res.mjs +97 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Behavior pair for `AggTestAggregate`. Create-once / reject-on-seen.
|
|
2
|
+
|
|
3
|
+
type state = NotCreated | Active
|
|
4
|
+
|
|
5
|
+
let initialState = NotCreated
|
|
6
|
+
|
|
7
|
+
let evolve = (_state, _event: AggTestAggregate.event) => Active
|
|
8
|
+
|
|
9
|
+
let decide = (state, command: AggTestAggregate.command): result<
|
|
10
|
+
array<AggTestAggregate.event>,
|
|
11
|
+
AggTestAggregate.error,
|
|
12
|
+
> =>
|
|
13
|
+
switch (state, command) {
|
|
14
|
+
| (Active, _) => Error(AlreadyExists)
|
|
15
|
+
| (NotCreated, Add({name})) => Ok([Added({name: name})])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let moduleUrl = "agg-test://AggTestAggregateBehavior"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function evolve(_state, _event) {
|
|
5
|
+
return "Active";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function decide(state, command) {
|
|
9
|
+
if (state === "NotCreated") {
|
|
10
|
+
return {
|
|
11
|
+
TAG: "Ok",
|
|
12
|
+
_0: [{
|
|
13
|
+
TAG: "Added",
|
|
14
|
+
name: command.name
|
|
15
|
+
}]
|
|
16
|
+
};
|
|
17
|
+
} else {
|
|
18
|
+
return {
|
|
19
|
+
TAG: "Error",
|
|
20
|
+
_0: "AlreadyExists"
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let initialState = "NotCreated";
|
|
26
|
+
|
|
27
|
+
let moduleUrl = "agg-test://AggTestAggregateBehavior";
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
initialState,
|
|
31
|
+
evolve,
|
|
32
|
+
decide,
|
|
33
|
+
moduleUrl,
|
|
34
|
+
}
|
|
35
|
+
/* No side effect */
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Integration regression guard for the Aggregate Lambda entry point.
|
|
2
|
+
//
|
|
3
|
+
// Drives the real `AggregateEntryPoint.mjs` against a DynamoDB Local EventLog
|
|
4
|
+
// table, with a test loader supplying a tiny `AggTestAggregate` spec/behavior
|
|
5
|
+
// pair in place of the production Lambda's `/var/task/node_modules` dynamic
|
|
6
|
+
// import. Covers the same regression class as the DCB entry-point test: the
|
|
7
|
+
// shell wires the compiled EventLog/Aggregate/CommandTopic functors and the
|
|
8
|
+
// CommandGenerator by structural contract, so any drift between the JS caller
|
|
9
|
+
// and the ReScript-compiled shapes (a renamed callback field, a shifted arg)
|
|
10
|
+
// re-breaks the AppSync command path. This test fails it as
|
|
11
|
+
// CommandAccepted -> Lambda:Unhandled.
|
|
12
|
+
//
|
|
13
|
+
// This is also the harness that unblocks moving the Aggregate functor wiring
|
|
14
|
+
// behind a typed core (docs/plans/minimize-lambda-entrypoint-mjs-shell.md,
|
|
15
|
+
// Tier 2.5): with it green, that refactor is verifiable end-to-end rather than
|
|
16
|
+
// only at compile time. Boots via the same Docker-gated
|
|
17
|
+
// `pnpm run test:integration` suite as the storage-runtime tests.
|
|
18
|
+
|
|
19
|
+
open JestGlobals
|
|
20
|
+
|
|
21
|
+
module H = AggIntegrationHarness
|
|
22
|
+
|
|
23
|
+
// Build handlers via the entry point's exported factory, dispatch one AppSync
|
|
24
|
+
// direct-invoke event, run the returned Effect through the request context the
|
|
25
|
+
// production handler supplies, and return the AppSync-shape outcome JSON.
|
|
26
|
+
let runOneAppSyncEvent: (string, JSON.t) => promise<JSON.t> = %raw(`
|
|
27
|
+
async (tableName, event) => {
|
|
28
|
+
const { buildHandlersForConfig } = await import(
|
|
29
|
+
"@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs"
|
|
30
|
+
);
|
|
31
|
+
const Effect = await import("effect/Effect");
|
|
32
|
+
const { tag: requestContextTag } = await import(
|
|
33
|
+
"@reventlessdev/reventless-core/src/RequestContext.res.mjs"
|
|
34
|
+
);
|
|
35
|
+
const { commandOutcomeToJson } = await import(
|
|
36
|
+
"@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs"
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const loadModule = async (specifier) => {
|
|
40
|
+
if (specifier === "agg-test://spec") return await import("./AggTestAggregate.res.mjs");
|
|
41
|
+
if (specifier === "agg-test://behavior") return await import("./AggTestAggregateBehavior.res.mjs");
|
|
42
|
+
throw new Error("unknown test specifier: " + specifier);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const config = {
|
|
46
|
+
handlers: [{
|
|
47
|
+
specModule: "agg-test://spec",
|
|
48
|
+
behaviorModule: "agg-test://behavior",
|
|
49
|
+
eventLogTable: tableName,
|
|
50
|
+
queueUrl: "https://sqs.eu-west-1.amazonaws.com/000000000000/agg-test-queue",
|
|
51
|
+
queueArn: "arn:aws:sqs:eu-west-1:000000000000:agg-test-queue",
|
|
52
|
+
}],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const [, cmdGenHandlers] = await buildHandlersForConfig(config, { loadModule });
|
|
56
|
+
const cmdGenHandler = cmdGenHandlers["AggTestAggregate"];
|
|
57
|
+
|
|
58
|
+
const effect = cmdGenHandler(event, {})
|
|
59
|
+
.pipe(Effect.provideService(requestContextTag, { correlationId: "agg-test" }));
|
|
60
|
+
const outcome = await Effect.runPromise(effect);
|
|
61
|
+
return commandOutcomeToJson(outcome);
|
|
62
|
+
}
|
|
63
|
+
`)
|
|
64
|
+
|
|
65
|
+
// CommandGenerator payload: `{command, arguments, meta}` — the shape AppSync's
|
|
66
|
+
// direct-invoke resolver sends. `command` is the constructor name as a string;
|
|
67
|
+
// `arguments.id` is the aggregate/EventLog id (stripped from the command params
|
|
68
|
+
// by makeGenerateCommand's stripIdFromParams).
|
|
69
|
+
let buildAppSyncEvent = (~id, ~name): JSON.t => {
|
|
70
|
+
let arguments = Dict.fromArray([
|
|
71
|
+
("id", id->JSON.Encode.string),
|
|
72
|
+
("name", name->JSON.Encode.string),
|
|
73
|
+
])
|
|
74
|
+
let meta = Dict.fromArray([
|
|
75
|
+
("user", "agg-test"->JSON.Encode.string),
|
|
76
|
+
("ip", JSON.Encode.null),
|
|
77
|
+
])
|
|
78
|
+
Dict.fromArray([
|
|
79
|
+
("command", "Add"->JSON.Encode.string),
|
|
80
|
+
("arguments", arguments->JSON.Encode.object),
|
|
81
|
+
("meta", meta->JSON.Encode.object),
|
|
82
|
+
])->JSON.Encode.object
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("AggregateEntryPoint integration", () => {
|
|
86
|
+
testAsync(
|
|
87
|
+
"AppSync direct-invoke routes through the runtime handler to CommandAccepted",
|
|
88
|
+
async () => {
|
|
89
|
+
// Own table-name prefix avoids collisions when sharing a DDB Local
|
|
90
|
+
// instance with sibling suites.
|
|
91
|
+
let table = await H.createEventLogTable("AggIt_" ++ Date.now()->Float.toString)
|
|
92
|
+
let event = buildAppSyncEvent(~id="agg-1", ~name="Widget One")
|
|
93
|
+
let outcomeJson = await runOneAppSyncEvent(table.name, event)
|
|
94
|
+
|
|
95
|
+
// A functor/arg-shape drift manifests as a thrown error from `runPromise`
|
|
96
|
+
// before `commandOutcomeToJson` runs, so reaching this assertion already
|
|
97
|
+
// proves the regression hasn't returned; we still assert CommandAccepted
|
|
98
|
+
// so a drift to silent rejection also fails.
|
|
99
|
+
let s = outcomeJson->JSON.stringifyAny->Option.getOr("<unserializable>")
|
|
100
|
+
expect(s->String.includes("CommandAccepted"))->toBe(true)
|
|
101
|
+
},
|
|
102
|
+
)
|
|
103
|
+
})
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as AggIntegrationHarness$ReventlessAws from "./AggIntegrationHarness.res.mjs";
|
|
5
|
+
|
|
6
|
+
let runOneAppSyncEvent = (async (tableName, event) => {
|
|
7
|
+
const { buildHandlersForConfig } = await import(
|
|
8
|
+
"@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs"
|
|
9
|
+
);
|
|
10
|
+
const Effect = await import("effect/Effect");
|
|
11
|
+
const { tag: requestContextTag } = await import(
|
|
12
|
+
"@reventlessdev/reventless-core/src/RequestContext.res.mjs"
|
|
13
|
+
);
|
|
14
|
+
const { commandOutcomeToJson } = await import(
|
|
15
|
+
"@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs"
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const loadModule = async (specifier) => {
|
|
19
|
+
if (specifier === "agg-test://spec") return await import("./AggTestAggregate.res.mjs");
|
|
20
|
+
if (specifier === "agg-test://behavior") return await import("./AggTestAggregateBehavior.res.mjs");
|
|
21
|
+
throw new Error("unknown test specifier: " + specifier);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const config = {
|
|
25
|
+
handlers: [{
|
|
26
|
+
specModule: "agg-test://spec",
|
|
27
|
+
behaviorModule: "agg-test://behavior",
|
|
28
|
+
eventLogTable: tableName,
|
|
29
|
+
queueUrl: "https://sqs.eu-west-1.amazonaws.com/000000000000/agg-test-queue",
|
|
30
|
+
queueArn: "arn:aws:sqs:eu-west-1:000000000000:agg-test-queue",
|
|
31
|
+
}],
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const [, cmdGenHandlers] = await buildHandlersForConfig(config, { loadModule });
|
|
35
|
+
const cmdGenHandler = cmdGenHandlers["AggTestAggregate"];
|
|
36
|
+
|
|
37
|
+
const effect = cmdGenHandler(event, {})
|
|
38
|
+
.pipe(Effect.provideService(requestContextTag, { correlationId: "agg-test" }));
|
|
39
|
+
const outcome = await Effect.runPromise(effect);
|
|
40
|
+
return commandOutcomeToJson(outcome);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function buildAppSyncEvent(id, name) {
|
|
44
|
+
let $$arguments = Object.fromEntries([
|
|
45
|
+
[
|
|
46
|
+
"id",
|
|
47
|
+
id
|
|
48
|
+
],
|
|
49
|
+
[
|
|
50
|
+
"name",
|
|
51
|
+
name
|
|
52
|
+
]
|
|
53
|
+
]);
|
|
54
|
+
let meta = Object.fromEntries([
|
|
55
|
+
[
|
|
56
|
+
"user",
|
|
57
|
+
"agg-test"
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
"ip",
|
|
61
|
+
null
|
|
62
|
+
]
|
|
63
|
+
]);
|
|
64
|
+
return Object.fromEntries([
|
|
65
|
+
[
|
|
66
|
+
"command",
|
|
67
|
+
"Add"
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"arguments",
|
|
71
|
+
$$arguments
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
"meta",
|
|
75
|
+
meta
|
|
76
|
+
]
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
globalThis.describe("AggregateEntryPoint integration", () => {
|
|
81
|
+
globalThis.test("AppSync direct-invoke routes through the runtime handler to CommandAccepted", async () => {
|
|
82
|
+
let table = await AggIntegrationHarness$ReventlessAws.createEventLogTable("AggIt_" + Date.now().toString());
|
|
83
|
+
let event = buildAppSyncEvent("agg-1", "Widget One");
|
|
84
|
+
let outcomeJson = await runOneAppSyncEvent(table.name, event);
|
|
85
|
+
let s = Stdlib_Option.getOr(JSON.stringify(outcomeJson), "<unserializable>");
|
|
86
|
+
globalThis.expect(s.includes("CommandAccepted")).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
let H;
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
H,
|
|
94
|
+
runOneAppSyncEvent,
|
|
95
|
+
buildAppSyncEvent,
|
|
96
|
+
}
|
|
97
|
+
/* runOneAppSyncEvent Not a pure module */
|