@reventlessdev/reventless-aws 3.0.0-alpha.332 → 3.0.0-alpha.333
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 +7 -0
- package/package.json +3 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +5 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +6 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +21 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +75 -8
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +40 -3
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +53 -11
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +30 -8
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +5 -3
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +6 -1
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +5 -3
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +6 -1
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +3 -1
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +4 -1
- package/tests/AutomationSliceEntryPoint_OpsTest.res +122 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +187 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.333 (2026-09-02)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **automation:** the slice registry rides in the archive, not the environment ([d03b42c](https://github.com/ReventlessDev/reventless-core/commit/d03b42cc074181fb6506f00e40205d617bc70ddb))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.332 (2026-09-01)
|
|
7
14
|
|
|
8
15
|
**Note:** Version bump only for package @reventlessdev/reventless-aws
|
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.333",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
21
21
|
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.7",
|
|
22
22
|
"@reventlessdev/rescript-node": "2.0.0-alpha.8",
|
|
23
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
24
23
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
24
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
25
25
|
"@reventlessdev/reventless-core": "3.0.0-alpha.253",
|
|
26
26
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.154",
|
|
27
|
-
"@reventlessdev/reventless-interop": "3.0.0-alpha.34",
|
|
28
27
|
"@reventlessdev/reventless-postgres": "3.0.0-alpha.117",
|
|
28
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.34",
|
|
29
29
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.126"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
@@ -278,9 +278,11 @@ let finish = () =>
|
|
|
278
278
|
|
|
279
279
|
let handlerConfigOutput =
|
|
280
280
|
Pulumi.Output.all(handlerOutputs)
|
|
281
|
-
->Pulumi.Output.apply(handlers =>
|
|
282
|
-
`{"handlers":[${handlers->Array.join(",")}]}`
|
|
283
|
-
|
|
281
|
+
->Pulumi.Output.apply(handlers => {
|
|
282
|
+
let json = `{"handlers":[${handlers->Array.join(",")}]}`
|
|
283
|
+
Util_LambdaEnvBudget.check(~lambdaName="AllAggregatesCmdHandler", ~handlerConfigJson=json)
|
|
284
|
+
json
|
|
285
|
+
})
|
|
284
286
|
|
|
285
287
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
286
288
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
@@ -14,6 +14,7 @@ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
|
14
14
|
import * as PgConnection$ReventlessAws from "../Postgres/PgConnection.res.mjs";
|
|
15
15
|
import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.mjs";
|
|
16
16
|
import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
|
|
17
|
+
import * as Util_LambdaEnvBudget$ReventlessAws from "../../util/Util_LambdaEnvBudget.res.mjs";
|
|
17
18
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
18
19
|
import * as CommandTopicChannel_SQS_Sync$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs";
|
|
19
20
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
@@ -184,7 +185,11 @@ function finish() {
|
|
|
184
185
|
]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + pluginFragment + param[3] + `}`);
|
|
185
186
|
handlerOutputs.push(handlerJson);
|
|
186
187
|
});
|
|
187
|
-
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers =>
|
|
188
|
+
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => {
|
|
189
|
+
let json = `{"handlers":[` + handlers.join(",") + `]}`;
|
|
190
|
+
Util_LambdaEnvBudget$ReventlessAws.check("AllAggregatesCmdHandler", json, undefined);
|
|
191
|
+
return json;
|
|
192
|
+
});
|
|
188
193
|
let envVars = {};
|
|
189
194
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
190
195
|
let tableName = pluginRmTableName.contents;
|
|
@@ -18,9 +18,21 @@ import { Make as outboundTranslationSliceCallbackMake } from "@reventlessdev/rev
|
|
|
18
18
|
import * as StreamOps from "./StreamRoutedEntryPoint_Ops.res.mjs";
|
|
19
19
|
import * as Ops from "./AutomationSliceEntryPoint_Ops.res.mjs";
|
|
20
20
|
import { runtimeExtensionsReady } from "./HandlerFactoryHelpers.mjs";
|
|
21
|
+
import { readFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
21
23
|
|
|
22
24
|
const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
|
|
23
25
|
|
|
26
|
+
// Absent whenever the config is full-key (tests, and any pre-split deployment),
|
|
27
|
+
// which is why a missing file is not an error.
|
|
28
|
+
const readSliceModules = () => {
|
|
29
|
+
try {
|
|
30
|
+
return readFileSync(join(process.cwd(), "automationSliceModules.json"), "utf-8");
|
|
31
|
+
} catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
24
36
|
async function buildAllHandlers() {
|
|
25
37
|
// Runtime extension seam: any registered out-of-tree extension gets its
|
|
26
38
|
// onColdStart before a single handler is built, which is what makes the
|
|
@@ -29,7 +41,15 @@ async function buildAllHandlers() {
|
|
|
29
41
|
await runtimeExtensionsReady;
|
|
30
42
|
const handlers = {};
|
|
31
43
|
const sweeps = [];
|
|
32
|
-
|
|
44
|
+
// The slice registry rides in the archive, not the environment — two module
|
|
45
|
+
// specifiers per slice outgrow Lambda's 4KB env-var ceiling (see the asset in
|
|
46
|
+
// AutomationSliceRuntime_Builder_Single). A full-key config still wins, so a
|
|
47
|
+
// caller that supplies one — every test that drives a config directly — needs
|
|
48
|
+
// no asset on disk. process.cwd() is /var/task, where the archive unpacks.
|
|
49
|
+
const entries = Ops.parseHandlerConfigWithModules(
|
|
50
|
+
readSliceModules(),
|
|
51
|
+
process.env["HANDLER_CONFIG"] || "",
|
|
52
|
+
);
|
|
33
53
|
|
|
34
54
|
await Promise.all(entries.map(async (entry) => {
|
|
35
55
|
if (!entry.bodyModule) {
|
|
@@ -62,16 +62,83 @@ let decodeEntry = (json: JSON.t): option<handlerEntry> =>
|
|
|
62
62
|
context: h->Dict.get("context")->Option.flatMap(decodeContext),
|
|
63
63
|
})
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
// A compact entry names its slice and carries only what the deploy resolved;
|
|
66
|
+
// the module paths and context live in the archive registry, keyed by the same
|
|
67
|
+
// name (AutomationSliceRuntime_Builder_Single). Shared `queueUrl` /
|
|
68
|
+
// `commandQueueIsFifo` / `urns` are hoisted out of the per-entry keys, since a
|
|
69
|
+
// slice publishing to the DCB fallback and reading its plugin's log repeats
|
|
70
|
+
// both verbatim — which is what outgrew the 4KB env limit.
|
|
71
|
+
let decodeCompactEntry = (
|
|
72
|
+
~modules: dict<JSON.t>,
|
|
73
|
+
~sharedQueueUrl: string,
|
|
74
|
+
~sharedIsFifo: option<bool>,
|
|
75
|
+
~sharedUrns: array<string>,
|
|
76
|
+
h: dict<JSON.t>,
|
|
77
|
+
name: string,
|
|
78
|
+
): handlerEntry => {
|
|
79
|
+
let m = modules->Dict.get(name)->Option.flatMap(JSON.Decode.object)->Option.getOr(Dict.make())
|
|
80
|
+
let urns =
|
|
81
|
+
h
|
|
82
|
+
->Dict.get("u")
|
|
83
|
+
->Option.flatMap(JSON.Decode.array)
|
|
84
|
+
->Option.map(a => a->Array.filterMap(JSON.Decode.string))
|
|
85
|
+
->Option.getOr(sharedUrns)
|
|
86
|
+
{
|
|
87
|
+
specModule: m->strOf("specModule")->Option.getOr(""),
|
|
88
|
+
bodyModule: m->strOf("bodyModule")->Option.getOr(""),
|
|
89
|
+
callbackType: m->strOf("callbackType")->Option.getOr("automation"),
|
|
90
|
+
queryDbTableName: h->strOf("q")->Option.getOr(""),
|
|
91
|
+
dcbQueueUrl: h->strOf("k")->Option.getOr(sharedQueueUrl),
|
|
92
|
+
commandQueueIsFifo: switch h->Dict.get("f")->Option.flatMap(JSON.Decode.bool) {
|
|
93
|
+
| Some(f) => Some(f)
|
|
94
|
+
| None => sharedIsFifo
|
|
95
|
+
},
|
|
96
|
+
// Only the first, which is all the shell registers today. The remaining
|
|
97
|
+
// urns stay in the config for the multi-source registration to use.
|
|
98
|
+
sourceUrn: urns->Array.get(0)->Option.getOr(""),
|
|
99
|
+
context: m->Dict.get("context")->Option.flatMap(decodeContext),
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** `modulesJson` is the archive-side slice registry (`""` when there is none);
|
|
104
|
+
an entry carrying its own `specModule` is a full-key config and needs it. */
|
|
105
|
+
let parseHandlerConfigWithModules = (modulesJson: string, rawJson: string): array<handlerEntry> =>
|
|
66
106
|
rawJson == ""
|
|
67
107
|
? []
|
|
68
|
-
:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
108
|
+
: {
|
|
109
|
+
let obj = rawJson->JSON.parseOrThrow->JSON.Decode.object->Option.getOr(Dict.make())
|
|
110
|
+
let modules =
|
|
111
|
+
modulesJson == ""
|
|
112
|
+
? Dict.make()
|
|
113
|
+
: modulesJson->JSON.parseOrThrow->JSON.Decode.object->Option.getOr(Dict.make())
|
|
114
|
+
let sharedQueueUrl = obj->strOf("queueUrl")->Option.getOr("")
|
|
115
|
+
let sharedIsFifo = obj->Dict.get("commandQueueIsFifo")->Option.flatMap(JSON.Decode.bool)
|
|
116
|
+
let sharedUrns =
|
|
117
|
+
obj
|
|
118
|
+
->Dict.get("urns")
|
|
119
|
+
->Option.flatMap(JSON.Decode.array)
|
|
120
|
+
->Option.map(a => a->Array.filterMap(JSON.Decode.string))
|
|
121
|
+
->Option.getOr([])
|
|
122
|
+
obj
|
|
123
|
+
->Dict.get("handlers")
|
|
124
|
+
->Option.flatMap(JSON.Decode.array)
|
|
125
|
+
->Option.getOr([])
|
|
126
|
+
->Array.filterMap(item =>
|
|
127
|
+
switch item->JSON.Decode.object {
|
|
128
|
+
| None => None
|
|
129
|
+
| Some(h) =>
|
|
130
|
+
switch h->strOf("n") {
|
|
131
|
+
| Some(name) =>
|
|
132
|
+
Some(decodeCompactEntry(~modules, ~sharedQueueUrl, ~sharedIsFifo, ~sharedUrns, h, name))
|
|
133
|
+
| None => decodeEntry(item)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The full-key form, for a config that carries its own module paths. */
|
|
140
|
+
let parseHandlerConfig = (rawJson: string): array<handlerEntry> =>
|
|
141
|
+
parseHandlerConfigWithModules("", rawJson)
|
|
75
142
|
|
|
76
143
|
// A pre-`bodyModule` config entry cannot rebuild the callback (the spec module
|
|
77
144
|
// alone lacks the mappings/process). Warn and skip rather than crash the whole
|
|
@@ -49,12 +49,47 @@ function decodeEntry(json) {
|
|
|
49
49
|
}));
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
function
|
|
52
|
+
function decodeCompactEntry(modules, sharedQueueUrl, sharedIsFifo, sharedUrns, h, name) {
|
|
53
|
+
let m = Stdlib_Option.getOr(Stdlib_Option.flatMap(modules[name], Stdlib_JSON.Decode.object), {});
|
|
54
|
+
let urns = Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(h["u"], Stdlib_JSON.Decode.array), a => Stdlib_Array.filterMap(a, Stdlib_JSON.Decode.string)), sharedUrns);
|
|
55
|
+
let f = Stdlib_Option.flatMap(h["f"], Stdlib_JSON.Decode.bool);
|
|
56
|
+
return {
|
|
57
|
+
specModule: Stdlib_Option.getOr(strOf(m, "specModule"), ""),
|
|
58
|
+
bodyModule: Stdlib_Option.getOr(strOf(m, "bodyModule"), ""),
|
|
59
|
+
callbackType: Stdlib_Option.getOr(strOf(m, "callbackType"), "automation"),
|
|
60
|
+
queryDbTableName: Stdlib_Option.getOr(strOf(h, "q"), ""),
|
|
61
|
+
dcbQueueUrl: Stdlib_Option.getOr(strOf(h, "k"), sharedQueueUrl),
|
|
62
|
+
commandQueueIsFifo: f !== undefined ? f : sharedIsFifo,
|
|
63
|
+
sourceUrn: Stdlib_Option.getOr(urns[0], ""),
|
|
64
|
+
context: Stdlib_Option.flatMap(m["context"], decodeContext)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseHandlerConfigWithModules(modulesJson, rawJson) {
|
|
53
69
|
if (rawJson === "") {
|
|
54
70
|
return [];
|
|
55
|
-
} else {
|
|
56
|
-
return Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(JSON.parse(rawJson)), obj => obj["handlers"]), Stdlib_JSON.Decode.array), []), decodeEntry);
|
|
57
71
|
}
|
|
72
|
+
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(rawJson)), {});
|
|
73
|
+
let modules = modulesJson === "" ? ({}) : Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(modulesJson)), {});
|
|
74
|
+
let sharedQueueUrl = Stdlib_Option.getOr(strOf(obj, "queueUrl"), "");
|
|
75
|
+
let sharedIsFifo = Stdlib_Option.flatMap(obj["commandQueueIsFifo"], Stdlib_JSON.Decode.bool);
|
|
76
|
+
let sharedUrns = Stdlib_Option.getOr(Stdlib_Option.map(Stdlib_Option.flatMap(obj["urns"], Stdlib_JSON.Decode.array), a => Stdlib_Array.filterMap(a, Stdlib_JSON.Decode.string)), []);
|
|
77
|
+
return Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["handlers"], Stdlib_JSON.Decode.array), []), item => {
|
|
78
|
+
let h = Stdlib_JSON.Decode.object(item);
|
|
79
|
+
if (h === undefined) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
let name = strOf(h, "n");
|
|
83
|
+
if (name !== undefined) {
|
|
84
|
+
return decodeCompactEntry(modules, sharedQueueUrl, sharedIsFifo, sharedUrns, h, name);
|
|
85
|
+
} else {
|
|
86
|
+
return decodeEntry(item);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function parseHandlerConfig(rawJson) {
|
|
92
|
+
return parseHandlerConfigWithModules("", rawJson);
|
|
58
93
|
}
|
|
59
94
|
|
|
60
95
|
function warnMissingBodyModule(entry) {
|
|
@@ -247,6 +282,8 @@ export {
|
|
|
247
282
|
strOf,
|
|
248
283
|
decodeContext,
|
|
249
284
|
decodeEntry,
|
|
285
|
+
decodeCompactEntry,
|
|
286
|
+
parseHandlerConfigWithModules,
|
|
250
287
|
parseHandlerConfig,
|
|
251
288
|
warnMissingBodyModule,
|
|
252
289
|
defaultContext,
|
|
@@ -542,6 +542,12 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
542
542
|
}
|
|
543
543
|
|
|
544
544
|
let handlerOutputs: array<Pulumi.Output.t<string>> = []
|
|
545
|
+
// The static half of each slice's config — module paths and context, all
|
|
546
|
+
// known synchronously. Rides in the code archive rather than the
|
|
547
|
+
// environment: two module specifiers per slice is the term that grows
|
|
548
|
+
// with the plugin, and it is what pushed this Lambda past AWS's 4KB env
|
|
549
|
+
// limit. Keyed by slice name, which is what the compact env entry names.
|
|
550
|
+
let sliceModuleEntries: array<string> = []
|
|
545
551
|
let packageDirs: dict<string> = Dict.make()
|
|
546
552
|
let allQueryDbResources: array<ReventlessInfra.Adapter.resource> = []
|
|
547
553
|
// Whether any slice publishes to the DCB fallback rather than a target
|
|
@@ -549,7 +555,7 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
549
555
|
// too, once, below the loop.
|
|
550
556
|
let anyDcbFallback = ref(false)
|
|
551
557
|
|
|
552
|
-
bundledInfos->Dict.forEachWithKey((info,
|
|
558
|
+
bundledInfos->Dict.forEachWithKey((info, name) => {
|
|
553
559
|
info.queryDbResources->Array.forEach(r => allQueryDbResources->Array.push(r)->ignore)
|
|
554
560
|
// The queue this slice publishes its command to. An Aggregate registers
|
|
555
561
|
// its CommandTopic while plugin construction is still synchronous —
|
|
@@ -582,6 +588,12 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
582
588
|
| None => ""
|
|
583
589
|
}
|
|
584
590
|
|
|
591
|
+
let nameJson = name->JSON.stringifyAny->Option.getOr(`""`)
|
|
592
|
+
let _ =
|
|
593
|
+
sliceModuleEntries->Array.push(
|
|
594
|
+
`${nameJson}:{"specModule":${specModule},"bodyModule":${bodyModule},"callbackType":${callbackType}${contextFragment}}`,
|
|
595
|
+
)
|
|
596
|
+
|
|
585
597
|
// Per slice, not plugin-wide: an outbound slice targeting an Aggregate
|
|
586
598
|
// publishes to that aggregate's CommandTopic. Everything else keeps the
|
|
587
599
|
// DCB topic, which is what the field has always meant — and with it the
|
|
@@ -590,17 +602,23 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
590
602
|
| Some({queueUrl, isFifo}) => (queueUrl, isFifo)
|
|
591
603
|
| None => (dcbQueueUrl, dcbQueueIsFifo.contents)
|
|
592
604
|
}
|
|
605
|
+
// What each slice may override is decided structurally, not from the
|
|
606
|
+
// resolved values: a slice listening only on the plugin's DCB log, and
|
|
607
|
+
// publishing to the DCB fallback, repeats the shared urn and queue URL
|
|
608
|
+
// exactly — so it emits neither and inherits both.
|
|
609
|
+
let hasOwnUrns =
|
|
610
|
+
!(info.consumesDcbLog) || info.sourceTopics->Dict.keysToArray->Array.length > 0
|
|
593
611
|
let handlerJson =
|
|
594
612
|
Pulumi.Output.all3((info.queryDbTableName, commandQueueUrl, sourceUrnsFor(info)))
|
|
595
613
|
->Pulumi.Output.apply(((tableName, queueUrl, urns)) => {
|
|
596
|
-
let
|
|
597
|
-
urns->Array.map(JSON.Encode.string)->JSON.Encode.array->JSON.stringify
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
`{"
|
|
614
|
+
let urnsFragment = hasOwnUrns
|
|
615
|
+
? `,"u":${urns->Array.map(JSON.Encode.string)->JSON.Encode.array->JSON.stringify}`
|
|
616
|
+
: ""
|
|
617
|
+
let queueFragment = switch target {
|
|
618
|
+
| Some(_) => `,"k":"${queueUrl}","f":${commandQueueIsFifo ? "true" : "false"}`
|
|
619
|
+
| None => ""
|
|
620
|
+
}
|
|
621
|
+
`{"n":${nameJson},"q":"${tableName}"${urnsFragment}${queueFragment}}`
|
|
604
622
|
})
|
|
605
623
|
let _ = handlerOutputs->Array.push(handlerJson)
|
|
606
624
|
})
|
|
@@ -613,9 +631,24 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
613
631
|
dcbQueueResources->Array.forEach(r => allQueryDbResources->Array.push(r)->ignore)
|
|
614
632
|
}
|
|
615
633
|
|
|
634
|
+
// The defaults every non-overriding slice inherits, carried once.
|
|
635
|
+
let sharedOutput =
|
|
636
|
+
Pulumi.Output.all2((dcbQueueUrl, dcbSourceUrn))->Pulumi.Output.apply(((
|
|
637
|
+
queueUrl,
|
|
638
|
+
urn,
|
|
639
|
+
)) => {
|
|
640
|
+
let fifoJson = dcbQueueIsFifo.contents ? "true" : "false"
|
|
641
|
+
let urnJson = urn->JSON.stringifyAny->Option.getOr(`""`)
|
|
642
|
+
`"queueUrl":"${queueUrl}","commandQueueIsFifo":${fifoJson},"urns":[${urnJson}]`
|
|
643
|
+
})
|
|
644
|
+
|
|
616
645
|
let handlerConfigOutput =
|
|
617
|
-
Pulumi.Output.all(handlerOutputs)
|
|
618
|
-
->Pulumi.Output.apply(handlers =>
|
|
646
|
+
Pulumi.Output.all2((sharedOutput, Pulumi.Output.all(handlerOutputs)))
|
|
647
|
+
->Pulumi.Output.apply(((shared, handlers)) => {
|
|
648
|
+
let json = `{${shared},"handlers":[${handlers->Array.join(",")}]}`
|
|
649
|
+
Util_LambdaEnvBudget.check(~lambdaName="AllAutomationSlices", ~handlerConfigJson=json)
|
|
650
|
+
json
|
|
651
|
+
})
|
|
619
652
|
|
|
620
653
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
621
654
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
@@ -630,9 +663,18 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
630
663
|
envVars->Dict.set(name, value->Pulumi.Output.asInput)
|
|
631
664
|
)
|
|
632
665
|
|
|
666
|
+
// Carries no Pulumi Output, so the archive is still built synchronously —
|
|
667
|
+
// the table names and urns that do are what stays in HANDLER_CONFIG.
|
|
668
|
+
let extraStringAssets = Dict.make()
|
|
669
|
+
extraStringAssets->Dict.set(
|
|
670
|
+
"automationSliceModules.json",
|
|
671
|
+
`{${sliceModuleEntries->Array.join(",")}}`,
|
|
672
|
+
)
|
|
673
|
+
|
|
633
674
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
634
675
|
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs",
|
|
635
676
|
~packageDirs,
|
|
677
|
+
~extraStringAssets,
|
|
636
678
|
)
|
|
637
679
|
|
|
638
680
|
let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
|
|
@@ -18,6 +18,7 @@ import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/
|
|
|
18
18
|
import * as Cloudwatch_EventRule$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/Cloudwatch/Cloudwatch_EventRule.res.mjs";
|
|
19
19
|
import * as Cloudwatch_EventTarget$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/Cloudwatch/Cloudwatch_EventTarget.res.mjs";
|
|
20
20
|
import * as CommandTopicRegistry$ReventlessAws from "../CommandTopic/CommandTopicRegistry.res.mjs";
|
|
21
|
+
import * as Util_LambdaEnvBudget$ReventlessAws from "../../util/Util_LambdaEnvBudget.res.mjs";
|
|
21
22
|
import * as PluginRuntime_Builder$ReventlessAws from "../../plugin/runtime/PluginRuntime_Builder.res.mjs";
|
|
22
23
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
23
24
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
@@ -279,12 +280,13 @@ function finishWithDcbEventLog(dcbEventLog) {
|
|
|
279
280
|
let url = dcbQueueUrlRef.contents;
|
|
280
281
|
let dcbQueueUrl = url !== undefined ? url : Pulumi.output("NOT_AVAILABLE");
|
|
281
282
|
let handlerOutputs = [];
|
|
283
|
+
let sliceModuleEntries = [];
|
|
282
284
|
let packageDirs = {};
|
|
283
285
|
let allQueryDbResources = [];
|
|
284
286
|
let anyDcbFallback = {
|
|
285
287
|
contents: false
|
|
286
288
|
};
|
|
287
|
-
Stdlib_Dict.forEachWithKey(bundledInfos, (info,
|
|
289
|
+
Stdlib_Dict.forEachWithKey(bundledInfos, (info, name) => {
|
|
288
290
|
info.queryDbResources.forEach(r => {
|
|
289
291
|
allQueryDbResources.push(r);
|
|
290
292
|
});
|
|
@@ -303,6 +305,8 @@ function finishWithDcbEventLog(dcbEventLog) {
|
|
|
303
305
|
let callbackType = Stdlib_Option.getOr(JSON.stringify(info.callbackType), `""`);
|
|
304
306
|
let context = info.context;
|
|
305
307
|
let contextFragment = context !== undefined ? Stdlib_Option.getOr(Stdlib_Option.map(JSON.stringify(context), json => `,"context":` + json), "") : "";
|
|
308
|
+
let nameJson = Stdlib_Option.getOr(JSON.stringify(name), `""`);
|
|
309
|
+
sliceModuleEntries.push(nameJson + `:{"specModule":` + specModule + `,"bodyModule":` + bodyModule + `,"callbackType":` + callbackType + contextFragment + `}`);
|
|
306
310
|
let match = target !== undefined ? [
|
|
307
311
|
target.queueUrl,
|
|
308
312
|
target.isFifo
|
|
@@ -311,16 +315,17 @@ function finishWithDcbEventLog(dcbEventLog) {
|
|
|
311
315
|
dcbQueueIsFifo.contents
|
|
312
316
|
];
|
|
313
317
|
let commandQueueIsFifo = match[1];
|
|
318
|
+
let hasOwnUrns = !info.consumesDcbLog || Object.keys(info.sourceTopics).length !== 0;
|
|
314
319
|
let handlerJson = Pulumi.all([
|
|
315
320
|
info.queryDbTableName,
|
|
316
321
|
match[0],
|
|
317
322
|
sourceUrnsFor(info)
|
|
318
323
|
]).apply(param => {
|
|
319
|
-
let
|
|
320
|
-
let
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return `{"
|
|
324
|
+
let urnsFragment = hasOwnUrns ? `,"u":` + JSON.stringify(param[2].map(prim => prim)) : "";
|
|
325
|
+
let queueFragment = target !== undefined ? `,"k":"` + param[1] + `","f":` + (
|
|
326
|
+
commandQueueIsFifo ? "true" : "false"
|
|
327
|
+
) : "";
|
|
328
|
+
return `{"n":` + nameJson + `,"q":"` + param[0] + `"` + urnsFragment + queueFragment + `}`;
|
|
324
329
|
});
|
|
325
330
|
handlerOutputs.push(handlerJson);
|
|
326
331
|
});
|
|
@@ -329,13 +334,30 @@ function finishWithDcbEventLog(dcbEventLog) {
|
|
|
329
334
|
allQueryDbResources.push(r);
|
|
330
335
|
});
|
|
331
336
|
}
|
|
332
|
-
let
|
|
337
|
+
let sharedOutput = Pulumi.all([
|
|
338
|
+
dcbQueueUrl,
|
|
339
|
+
dcbSourceUrn
|
|
340
|
+
]).apply(param => {
|
|
341
|
+
let fifoJson = dcbQueueIsFifo.contents ? "true" : "false";
|
|
342
|
+
let urnJson = Stdlib_Option.getOr(JSON.stringify(param[1]), `""`);
|
|
343
|
+
return `"queueUrl":"` + param[0] + `","commandQueueIsFifo":` + fifoJson + `,"urns":[` + urnJson + `]`;
|
|
344
|
+
});
|
|
345
|
+
let handlerConfigOutput = Pulumi.all([
|
|
346
|
+
sharedOutput,
|
|
347
|
+
Pulumi.all(handlerOutputs)
|
|
348
|
+
]).apply(param => {
|
|
349
|
+
let json = `{` + param[0] + `,"handlers":[` + param[1].join(",") + `]}`;
|
|
350
|
+
Util_LambdaEnvBudget$ReventlessAws.check("AllAutomationSlices", json, undefined);
|
|
351
|
+
return json;
|
|
352
|
+
});
|
|
333
353
|
let envVars = {};
|
|
334
354
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
335
355
|
PluginRuntime_Builder$ReventlessAws.capabilityEnv().forEach(param => {
|
|
336
356
|
envVars[param[0]] = param[1];
|
|
337
357
|
});
|
|
338
|
-
let
|
|
358
|
+
let extraStringAssets = {};
|
|
359
|
+
extraStringAssets["automationSliceModules.json"] = `{` + sliceModuleEntries.join(",") + `}`;
|
|
360
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, extraStringAssets, undefined);
|
|
339
361
|
let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAutomationSlices", "Reactor", "AutomationSlice", match.code, match.sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, undefined, opts);
|
|
340
362
|
EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllAutomationSlices", [{
|
|
341
363
|
channel: channel,
|
|
@@ -251,9 +251,11 @@ let finish = () =>
|
|
|
251
251
|
|
|
252
252
|
let handlerConfigOutput =
|
|
253
253
|
Pulumi.Output.all(handlerOutputs)
|
|
254
|
-
->Pulumi.Output.apply(handlers =>
|
|
255
|
-
`{"handlers":[${handlers->Array.join(",")}]}`
|
|
256
|
-
|
|
254
|
+
->Pulumi.Output.apply(handlers => {
|
|
255
|
+
let json = `{"handlers":[${handlers->Array.join(",")}]}`
|
|
256
|
+
Util_LambdaEnvBudget.check(~lambdaName="AllReadModels", ~handlerConfigJson=json)
|
|
257
|
+
json
|
|
258
|
+
})
|
|
257
259
|
|
|
258
260
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
259
261
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
@@ -18,6 +18,7 @@ import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.
|
|
|
18
18
|
import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
|
|
19
19
|
import * as PgProjectionFeed$ReventlessAws from "../Postgres/PgProjectionFeed.res.mjs";
|
|
20
20
|
import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
|
|
21
|
+
import * as Util_LambdaEnvBudget$ReventlessAws from "../../util/Util_LambdaEnvBudget.res.mjs";
|
|
21
22
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
22
23
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
23
24
|
|
|
@@ -142,7 +143,11 @@ function finish() {
|
|
|
142
143
|
});
|
|
143
144
|
handlerOutputs.push(handlerJson);
|
|
144
145
|
});
|
|
145
|
-
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers =>
|
|
146
|
+
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => {
|
|
147
|
+
let json = `{"handlers":[` + handlers.join(",") + `]}`;
|
|
148
|
+
Util_LambdaEnvBudget$ReventlessAws.check("AllReadModels", json, undefined);
|
|
149
|
+
return json;
|
|
150
|
+
});
|
|
146
151
|
let envVars = {};
|
|
147
152
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
148
153
|
let anyPgStream = Object.keys(readModelInfos).some(rmName => {
|
|
@@ -153,9 +153,11 @@ let finish = () =>
|
|
|
153
153
|
|
|
154
154
|
let handlerConfigOutput =
|
|
155
155
|
Pulumi.Output.all(handlerOutputs)
|
|
156
|
-
->Pulumi.Output.apply(handlers =>
|
|
157
|
-
`{"handlers":[${handlers->Array.join(",")}]}`
|
|
158
|
-
|
|
156
|
+
->Pulumi.Output.apply(handlers => {
|
|
157
|
+
let json = `{"handlers":[${handlers->Array.join(",")}]}`
|
|
158
|
+
Util_LambdaEnvBudget.check(~lambdaName="AllSideEffectHandlers", ~handlerConfigJson=json)
|
|
159
|
+
json
|
|
160
|
+
})
|
|
159
161
|
|
|
160
162
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
161
163
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
@@ -11,6 +11,7 @@ import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/co
|
|
|
11
11
|
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
12
12
|
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
13
13
|
import * as Util_LogAttribution$ReventlessAws from "../../util/Util_LogAttribution.res.mjs";
|
|
14
|
+
import * as Util_LambdaEnvBudget$ReventlessAws from "../../util/Util_LambdaEnvBudget.res.mjs";
|
|
14
15
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
15
16
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
16
17
|
|
|
@@ -109,7 +110,11 @@ function finish() {
|
|
|
109
110
|
});
|
|
110
111
|
handlerOutputs.push(handlerJson);
|
|
111
112
|
});
|
|
112
|
-
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers =>
|
|
113
|
+
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => {
|
|
114
|
+
let json = `{"handlers":[` + handlers.join(",") + `]}`;
|
|
115
|
+
Util_LambdaEnvBudget$ReventlessAws.check("AllSideEffectHandlers", json, undefined);
|
|
116
|
+
return json;
|
|
117
|
+
});
|
|
113
118
|
let envVars = {};
|
|
114
119
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
115
120
|
Stdlib_Dict.forEachWithKey(extraEnvVarsAll, (v, k) => {
|
|
@@ -222,7 +222,9 @@ let buildLambda = (
|
|
|
222
222
|
->Array.join(",")
|
|
223
223
|
let baseJson = base->JSON.stringifyAny->Option.getOr(`""`)
|
|
224
224
|
let urnJson = sharedUrn->JSON.stringifyAny->Option.getOr(`""`)
|
|
225
|
-
`{"v":2,"base":${baseJson},"sourceUrn":${urnJson},"handlers":[${entries}]${pgFragment}}`
|
|
225
|
+
let json = `{"v":2,"base":${baseJson},"sourceUrn":${urnJson},"handlers":[${entries}]${pgFragment}}`
|
|
226
|
+
Util_LambdaEnvBudget.check(~lambdaName="AllStateViewSlices", ~handlerConfigJson=json)
|
|
227
|
+
json
|
|
226
228
|
})
|
|
227
229
|
|
|
228
230
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
@@ -18,6 +18,7 @@ import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/
|
|
|
18
18
|
import * as QueryDbBackend$ReventlessAws from "../QueryDb/QueryDbBackend.res.mjs";
|
|
19
19
|
import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
|
|
20
20
|
import * as PgProjectionFeed$ReventlessAws from "../Postgres/PgProjectionFeed.res.mjs";
|
|
21
|
+
import * as Util_LambdaEnvBudget$ReventlessAws from "../../util/Util_LambdaEnvBudget.res.mjs";
|
|
21
22
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
22
23
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
23
24
|
|
|
@@ -144,7 +145,9 @@ function buildLambda(parent, handlerOutputs, packageDirs, channelSpecs, feedQueu
|
|
|
144
145
|
}).join(",");
|
|
145
146
|
let baseJson = Stdlib_Option.getOr(JSON.stringify(base), `""`);
|
|
146
147
|
let urnJson = Stdlib_Option.getOr(JSON.stringify(sharedUrn), `""`);
|
|
147
|
-
|
|
148
|
+
let json = `{"v":2,"base":` + baseJson + `,"sourceUrn":` + urnJson + `,"handlers":[` + entries + `]` + param[1] + `}`;
|
|
149
|
+
Util_LambdaEnvBudget$ReventlessAws.check("AllStateViewSlices", json, undefined);
|
|
150
|
+
return json;
|
|
148
151
|
});
|
|
149
152
|
let envVars = {};
|
|
150
153
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
@@ -83,6 +83,128 @@ describe("AutomationSliceEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
83
83
|
})
|
|
84
84
|
})
|
|
85
85
|
|
|
86
|
+
// The compact form the builder emits: module paths and context ride in the
|
|
87
|
+
// archive registry, and the DCB queue + source urn are hoisted out of every
|
|
88
|
+
// entry that does not override them. Together these are what keep the Lambda's
|
|
89
|
+
// environment under AWS's 4KB ceiling as a plugin gains slices.
|
|
90
|
+
describe("AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules", () => {
|
|
91
|
+
let modules =
|
|
92
|
+
obj([
|
|
93
|
+
(
|
|
94
|
+
"Restock",
|
|
95
|
+
obj([
|
|
96
|
+
("specModule", str("@x/plugin/src/Order/AutomationSlice/Restock.res.mjs")),
|
|
97
|
+
("bodyModule", str("@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs")),
|
|
98
|
+
("callbackType", str("automation")),
|
|
99
|
+
(
|
|
100
|
+
"context",
|
|
101
|
+
obj([
|
|
102
|
+
("environment", str("alpha")),
|
|
103
|
+
("platformName", str("OnlineShop")),
|
|
104
|
+
("pluginName", str("Ordering")),
|
|
105
|
+
("sliceName", str("Restock")),
|
|
106
|
+
]),
|
|
107
|
+
),
|
|
108
|
+
]),
|
|
109
|
+
),
|
|
110
|
+
(
|
|
111
|
+
"Ship",
|
|
112
|
+
obj([
|
|
113
|
+
("specModule", str("@x/plugin/src/Order/OutboundTranslationSlice/Ship.res.mjs")),
|
|
114
|
+
("bodyModule", str("@x/plugin/src/Order/OutboundTranslationSlice/Ship_Translation.res.mjs")),
|
|
115
|
+
("callbackType", str("outbound")),
|
|
116
|
+
]),
|
|
117
|
+
),
|
|
118
|
+
])->JSON.stringify
|
|
119
|
+
|
|
120
|
+
let compactConfig = handlers =>
|
|
121
|
+
obj([
|
|
122
|
+
("queueUrl", str("https://sqs/dcb")),
|
|
123
|
+
("commandQueueIsFifo", JSON.Encode.bool(false)),
|
|
124
|
+
("urns", JSON.Encode.array([str("arn:dcb:log")])),
|
|
125
|
+
("handlers", JSON.Encode.array(handlers)),
|
|
126
|
+
])->JSON.stringify
|
|
127
|
+
|
|
128
|
+
testSync("a compact entry takes its modules from the registry and the shared defaults", () => {
|
|
129
|
+
let config = compactConfig([obj([("n", str("Restock")), ("q", str("RestockTodo-abc"))])])
|
|
130
|
+
let e =
|
|
131
|
+
AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
|
|
132
|
+
modules,
|
|
133
|
+
config,
|
|
134
|
+
)->Array.getUnsafe(0)
|
|
135
|
+
expect(e.specModule)->toBe("@x/plugin/src/Order/AutomationSlice/Restock.res.mjs")
|
|
136
|
+
expect(e.bodyModule)->toBe("@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs")
|
|
137
|
+
expect(e.callbackType)->toBe("automation")
|
|
138
|
+
expect(e.queryDbTableName)->toBe("RestockTodo-abc")
|
|
139
|
+
expect(e.dcbQueueUrl)->toBe("https://sqs/dcb")
|
|
140
|
+
expect(e.commandQueueIsFifo)->toEqual(Some(false))
|
|
141
|
+
expect(e.sourceUrn)->toBe("arn:dcb:log")
|
|
142
|
+
switch e.context {
|
|
143
|
+
| Some(ctx) => expect(ctx.sliceName)->toBe("Restock")
|
|
144
|
+
| None => JsError.throwWithMessage("expected Some(context)")
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
testSync("per-entry queue and urn override the shared defaults", () => {
|
|
149
|
+
let config = compactConfig([
|
|
150
|
+
obj([
|
|
151
|
+
("n", str("Ship")),
|
|
152
|
+
("q", str("ShipTodo-abc")),
|
|
153
|
+
("u", JSON.Encode.array([str("arn:aggregate:orders"), str("arn:dcb:log")])),
|
|
154
|
+
("k", str("https://sqs/orders.fifo")),
|
|
155
|
+
("f", JSON.Encode.bool(true)),
|
|
156
|
+
]),
|
|
157
|
+
])
|
|
158
|
+
let e =
|
|
159
|
+
AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
|
|
160
|
+
modules,
|
|
161
|
+
config,
|
|
162
|
+
)->Array.getUnsafe(0)
|
|
163
|
+
expect(e.callbackType)->toBe("outbound")
|
|
164
|
+
expect(e.dcbQueueUrl)->toBe("https://sqs/orders.fifo")
|
|
165
|
+
expect(e.commandQueueIsFifo)->toEqual(Some(true))
|
|
166
|
+
// The first of its own urns, not the shared one.
|
|
167
|
+
expect(e.sourceUrn)->toBe("arn:aggregate:orders")
|
|
168
|
+
expect(e.context->Option.isNone)->toBe(true)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
testSync("a compact entry with no registry match yields no bodyModule to skip on", () => {
|
|
172
|
+
let config = compactConfig([obj([("n", str("Gone")), ("q", str("GoneTodo-abc"))])])
|
|
173
|
+
let e =
|
|
174
|
+
AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
|
|
175
|
+
modules,
|
|
176
|
+
config,
|
|
177
|
+
)->Array.getUnsafe(0)
|
|
178
|
+
expect(e.specModule)->toBe("")
|
|
179
|
+
expect(e.bodyModule)->toBe("")
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
testSync("full-key entries still decode when a registry is present", () => {
|
|
183
|
+
let config = obj([
|
|
184
|
+
(
|
|
185
|
+
"handlers",
|
|
186
|
+
JSON.Encode.array([
|
|
187
|
+
obj([
|
|
188
|
+
("specModule", str("a.res.mjs")),
|
|
189
|
+
("bodyModule", str("b.res.mjs")),
|
|
190
|
+
("callbackType", str("outbound")),
|
|
191
|
+
("queryDbTableName", str("t")),
|
|
192
|
+
("dcbQueueUrl", str("u")),
|
|
193
|
+
("sourceUrn", str("arn")),
|
|
194
|
+
]),
|
|
195
|
+
]),
|
|
196
|
+
),
|
|
197
|
+
])->JSON.stringify
|
|
198
|
+
let e =
|
|
199
|
+
AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules(
|
|
200
|
+
modules,
|
|
201
|
+
config,
|
|
202
|
+
)->Array.getUnsafe(0)
|
|
203
|
+
expect(e.specModule)->toBe("a.res.mjs")
|
|
204
|
+
expect(e.dcbQueueUrl)->toBe("u")
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
|
|
86
208
|
// Shared stubs — the pipelines only touch phase1/phase2 plus the injected
|
|
87
209
|
// sync/publish, so the callback records carry empty TODO dicts.
|
|
88
210
|
let noopPublish: ReventlessCore.CommandTopic.publishJsons = async _ => ()
|
|
@@ -119,6 +119,193 @@ globalThis.describe("AutomationSliceEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
+
globalThis.describe("AutomationSliceEntryPoint_Ops.parseHandlerConfigWithModules", () => {
|
|
123
|
+
let modules = JSON.stringify(Object.fromEntries([
|
|
124
|
+
[
|
|
125
|
+
"Restock",
|
|
126
|
+
Object.fromEntries([
|
|
127
|
+
[
|
|
128
|
+
"specModule",
|
|
129
|
+
"@x/plugin/src/Order/AutomationSlice/Restock.res.mjs"
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
"bodyModule",
|
|
133
|
+
"@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs"
|
|
134
|
+
],
|
|
135
|
+
[
|
|
136
|
+
"callbackType",
|
|
137
|
+
"automation"
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
"context",
|
|
141
|
+
Object.fromEntries([
|
|
142
|
+
[
|
|
143
|
+
"environment",
|
|
144
|
+
"alpha"
|
|
145
|
+
],
|
|
146
|
+
[
|
|
147
|
+
"platformName",
|
|
148
|
+
"OnlineShop"
|
|
149
|
+
],
|
|
150
|
+
[
|
|
151
|
+
"pluginName",
|
|
152
|
+
"Ordering"
|
|
153
|
+
],
|
|
154
|
+
[
|
|
155
|
+
"sliceName",
|
|
156
|
+
"Restock"
|
|
157
|
+
]
|
|
158
|
+
])
|
|
159
|
+
]
|
|
160
|
+
])
|
|
161
|
+
],
|
|
162
|
+
[
|
|
163
|
+
"Ship",
|
|
164
|
+
Object.fromEntries([
|
|
165
|
+
[
|
|
166
|
+
"specModule",
|
|
167
|
+
"@x/plugin/src/Order/OutboundTranslationSlice/Ship.res.mjs"
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
"bodyModule",
|
|
171
|
+
"@x/plugin/src/Order/OutboundTranslationSlice/Ship_Translation.res.mjs"
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
"callbackType",
|
|
175
|
+
"outbound"
|
|
176
|
+
]
|
|
177
|
+
])
|
|
178
|
+
]
|
|
179
|
+
]));
|
|
180
|
+
let compactConfig = handlers => JSON.stringify(Object.fromEntries([
|
|
181
|
+
[
|
|
182
|
+
"queueUrl",
|
|
183
|
+
"https://sqs/dcb"
|
|
184
|
+
],
|
|
185
|
+
[
|
|
186
|
+
"commandQueueIsFifo",
|
|
187
|
+
false
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
"urns",
|
|
191
|
+
["arn:dcb:log"]
|
|
192
|
+
],
|
|
193
|
+
[
|
|
194
|
+
"handlers",
|
|
195
|
+
handlers
|
|
196
|
+
]
|
|
197
|
+
]));
|
|
198
|
+
globalThis.test("a compact entry takes its modules from the registry and the shared defaults", () => {
|
|
199
|
+
let config = compactConfig([Object.fromEntries([
|
|
200
|
+
[
|
|
201
|
+
"n",
|
|
202
|
+
"Restock"
|
|
203
|
+
],
|
|
204
|
+
[
|
|
205
|
+
"q",
|
|
206
|
+
"RestockTodo-abc"
|
|
207
|
+
]
|
|
208
|
+
])]);
|
|
209
|
+
let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
|
|
210
|
+
globalThis.expect(e.specModule).toBe("@x/plugin/src/Order/AutomationSlice/Restock.res.mjs");
|
|
211
|
+
globalThis.expect(e.bodyModule).toBe("@x/plugin/src/Order/AutomationSlice/Restock_Automation.res.mjs");
|
|
212
|
+
globalThis.expect(e.callbackType).toBe("automation");
|
|
213
|
+
globalThis.expect(e.queryDbTableName).toBe("RestockTodo-abc");
|
|
214
|
+
globalThis.expect(e.dcbQueueUrl).toBe("https://sqs/dcb");
|
|
215
|
+
globalThis.expect(e.commandQueueIsFifo).toEqual(false);
|
|
216
|
+
globalThis.expect(e.sourceUrn).toBe("arn:dcb:log");
|
|
217
|
+
let ctx = e.context;
|
|
218
|
+
if (ctx !== undefined) {
|
|
219
|
+
globalThis.expect(ctx.sliceName).toBe("Restock");
|
|
220
|
+
return;
|
|
221
|
+
} else {
|
|
222
|
+
return Stdlib_JsError.throwWithMessage("expected Some(context)");
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
globalThis.test("per-entry queue and urn override the shared defaults", () => {
|
|
226
|
+
let config = compactConfig([Object.fromEntries([
|
|
227
|
+
[
|
|
228
|
+
"n",
|
|
229
|
+
"Ship"
|
|
230
|
+
],
|
|
231
|
+
[
|
|
232
|
+
"q",
|
|
233
|
+
"ShipTodo-abc"
|
|
234
|
+
],
|
|
235
|
+
[
|
|
236
|
+
"u",
|
|
237
|
+
[
|
|
238
|
+
"arn:aggregate:orders",
|
|
239
|
+
"arn:dcb:log"
|
|
240
|
+
]
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
"k",
|
|
244
|
+
"https://sqs/orders.fifo"
|
|
245
|
+
],
|
|
246
|
+
[
|
|
247
|
+
"f",
|
|
248
|
+
true
|
|
249
|
+
]
|
|
250
|
+
])]);
|
|
251
|
+
let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
|
|
252
|
+
globalThis.expect(e.callbackType).toBe("outbound");
|
|
253
|
+
globalThis.expect(e.dcbQueueUrl).toBe("https://sqs/orders.fifo");
|
|
254
|
+
globalThis.expect(e.commandQueueIsFifo).toEqual(true);
|
|
255
|
+
globalThis.expect(e.sourceUrn).toBe("arn:aggregate:orders");
|
|
256
|
+
globalThis.expect(Stdlib_Option.isNone(e.context)).toBe(true);
|
|
257
|
+
});
|
|
258
|
+
globalThis.test("a compact entry with no registry match yields no bodyModule to skip on", () => {
|
|
259
|
+
let config = compactConfig([Object.fromEntries([
|
|
260
|
+
[
|
|
261
|
+
"n",
|
|
262
|
+
"Gone"
|
|
263
|
+
],
|
|
264
|
+
[
|
|
265
|
+
"q",
|
|
266
|
+
"GoneTodo-abc"
|
|
267
|
+
]
|
|
268
|
+
])]);
|
|
269
|
+
let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
|
|
270
|
+
globalThis.expect(e.specModule).toBe("");
|
|
271
|
+
globalThis.expect(e.bodyModule).toBe("");
|
|
272
|
+
});
|
|
273
|
+
globalThis.test("full-key entries still decode when a registry is present", () => {
|
|
274
|
+
let config = JSON.stringify(Object.fromEntries([[
|
|
275
|
+
"handlers",
|
|
276
|
+
[Object.fromEntries([
|
|
277
|
+
[
|
|
278
|
+
"specModule",
|
|
279
|
+
"a.res.mjs"
|
|
280
|
+
],
|
|
281
|
+
[
|
|
282
|
+
"bodyModule",
|
|
283
|
+
"b.res.mjs"
|
|
284
|
+
],
|
|
285
|
+
[
|
|
286
|
+
"callbackType",
|
|
287
|
+
"outbound"
|
|
288
|
+
],
|
|
289
|
+
[
|
|
290
|
+
"queryDbTableName",
|
|
291
|
+
"t"
|
|
292
|
+
],
|
|
293
|
+
[
|
|
294
|
+
"dcbQueueUrl",
|
|
295
|
+
"u"
|
|
296
|
+
],
|
|
297
|
+
[
|
|
298
|
+
"sourceUrn",
|
|
299
|
+
"arn"
|
|
300
|
+
]
|
|
301
|
+
])]
|
|
302
|
+
]]));
|
|
303
|
+
let e = AutomationSliceEntryPoint_Ops$ReventlessAws.parseHandlerConfigWithModules(modules, config)[0];
|
|
304
|
+
globalThis.expect(e.specModule).toBe("a.res.mjs");
|
|
305
|
+
globalThis.expect(e.dcbQueueUrl).toBe("u");
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
122
309
|
async function noopPublish(param) {
|
|
123
310
|
|
|
124
311
|
}
|