@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.
- package/CHANGELOG.md +23 -0
- package/package.json +7 -7
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
- package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +14 -1
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +22 -2
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +15 -4
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
- package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
- package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +26 -6
- package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +5 -2
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +1 -1
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
- package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
- package/src/components/AutomationSlice_Builder.res +2 -0
- package/src/components/AutomationSlice_Builder.res.mjs +1 -1
- package/src/components/OutboundTranslationSlice_Builder.res +1 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/src/plugin/runtime/PluginRuntime_Builder.res +13 -4
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +1 -1
- package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
- package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
- package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
- package/tests/CounterEntryPoint_OpsTest.res +115 -0
- package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
- package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
- package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
- package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
- package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
- package/tests/Platform_AdminScan_OpsTest.res +80 -0
- package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
- package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
- package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
- package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
- package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
- package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
|
@@ -1,150 +1,30 @@
|
|
|
1
1
|
// TaskBucket Lambda entry point.
|
|
2
|
-
// At cold start: reads HANDLER_CONFIG, dynamically imports callback
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
// At cold start: reads HANDLER_CONFIG, dynamically imports the task callback
|
|
3
|
+
// module, and dispatches the resulting task actions (command publishing, CW
|
|
4
|
+
// Events schedules).
|
|
5
|
+
//
|
|
6
|
+
// Thin untyped shell ("typed core, thin shell" —
|
|
7
|
+
// docs/plans/minimize-lambda-entrypoint-mjs-shell.md): this file owns ONLY the
|
|
8
|
+
// inherently-untyped seam — the dynamic `import()` of the callback module named
|
|
9
|
+
// in HANDLER_CONFIG and the read of its `callback` export. Everything else
|
|
10
|
+
// (HANDLER_CONFIG parsing, S3-event handling via TaskBucket_S3_Runtime, the
|
|
11
|
+
// exhaustive task-action dispatch, per-aggregate command publishing, the
|
|
12
|
+
// CloudWatch Events scheduler ops) lives type-checked in
|
|
13
|
+
// TaskBucketEntryPoint_Ops.res.
|
|
14
|
+
|
|
15
|
+
import * as Ops from "./TaskBucketEntryPoint_Ops.res.mjs";
|
|
15
16
|
|
|
16
17
|
const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
|
|
17
18
|
|
|
18
|
-
// Mirrors ScheduledPublisher_CloudWatchEvents_Runtime.toScheduleExpression — kept
|
|
19
|
-
// in sync manually because that runtime helper isn't bundled in this Lambda.
|
|
20
|
-
function toScheduleExpression(rate) {
|
|
21
|
-
// rate is a serialized variant from Reventless.Schedule.rate
|
|
22
|
-
switch (rate.TAG) {
|
|
23
|
-
case "Single": {
|
|
24
|
-
const [year, month, day, hour, minute] = [rate._0, rate._1, rate._2, rate._3, rate._4];
|
|
25
|
-
return `cron(${minute} ${hour} ${day} ${month} ? ${year})`;
|
|
26
|
-
}
|
|
27
|
-
case "Minutes": {
|
|
28
|
-
const n = rate._0;
|
|
29
|
-
return `rate(${n} minute${n === 1 ? "" : "s"})`;
|
|
30
|
-
}
|
|
31
|
-
case "Hours": {
|
|
32
|
-
const n = rate._0;
|
|
33
|
-
return `rate(${n} hour${n === 1 ? "" : "s"})`;
|
|
34
|
-
}
|
|
35
|
-
case "Days": {
|
|
36
|
-
const n = rate._0;
|
|
37
|
-
return `rate(${n} day${n === 1 ? "" : "s"})`;
|
|
38
|
-
}
|
|
39
|
-
case "Daily":
|
|
40
|
-
return `cron(${rate._1} ${rate._0} * * * *)`;
|
|
41
|
-
case "Weekdays":
|
|
42
|
-
return `cron(${rate._1} ${rate._0} ? * MON-FRI *)`;
|
|
43
|
-
case "WeekdaysAndSaturday":
|
|
44
|
-
return `cron(${rate._1} ${rate._0} ? * MON-SAT *)`;
|
|
45
|
-
default:
|
|
46
|
-
throw new Error(`TaskBucketEntryPoint: unknown schedule rate ${JSON.stringify(rate)}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
let cachedScheduler = null;
|
|
51
|
-
function makeSchedulerOps(schedulerCfg) {
|
|
52
|
-
if (!schedulerCfg) return null;
|
|
53
|
-
if (cachedScheduler) return cachedScheduler;
|
|
54
|
-
const roleArn = process.env[schedulerCfg.roleArnEnv];
|
|
55
|
-
const targetArn = process.env[schedulerCfg.targetArnEnv];
|
|
56
|
-
const targetName = process.env[schedulerCfg.targetNameEnv];
|
|
57
|
-
if (!roleArn || !targetArn || !targetName) {
|
|
58
|
-
log.warn("scheduler env vars missing — schedules will no-op", { comp: "TaskBucketRuntime" });
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
const client = new CloudWatchEventsClient({});
|
|
62
|
-
cachedScheduler = {
|
|
63
|
-
createSchedule: async (schedule) => {
|
|
64
|
-
await client.send(new PutRuleCommand({
|
|
65
|
-
Name: schedule.name,
|
|
66
|
-
ScheduleExpression: toScheduleExpression(schedule.rate),
|
|
67
|
-
RoleArn: roleArn,
|
|
68
|
-
State: "ENABLED",
|
|
69
|
-
}));
|
|
70
|
-
await client.send(new PutTargetsCommand({
|
|
71
|
-
Rule: schedule.name,
|
|
72
|
-
Targets: [{ Arn: targetArn, Id: targetName, Input: schedule.payload }],
|
|
73
|
-
}));
|
|
74
|
-
},
|
|
75
|
-
deleteSchedule: async (name) => {
|
|
76
|
-
await client.send(new RemoveTargetsCommand({ Rule: name, Ids: [targetName] }));
|
|
77
|
-
await client.send(new DeleteRuleCommand({ Name: name }));
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
return cachedScheduler;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
19
|
async function buildHandler() {
|
|
84
|
-
const
|
|
85
|
-
const config = JSON.parse(configStr);
|
|
86
|
-
|
|
20
|
+
const config = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "");
|
|
87
21
|
const callbackModule = await dynamicImport(config.callbackModule);
|
|
88
|
-
|
|
89
|
-
const handleEvents = handleBucketEvent(bucketCallback);
|
|
90
|
-
|
|
91
|
-
// Build publishCommands dict from env var queue URLs. The deploy-side builder
|
|
92
|
-
// writes HANDLER_CONFIG.publishToAggregates as { aggregateName: envVarName }
|
|
93
|
-
// (see TaskRuntime_Builder_PerBucket.res), so iterate accordingly.
|
|
94
|
-
const publishCommandsFns = {};
|
|
95
|
-
for (const [aggName, envVarName] of Object.entries(config.publishToAggregates || {})) {
|
|
96
|
-
const queueUrl = process.env[envVarName] || "";
|
|
97
|
-
if (queueUrl !== "") {
|
|
98
|
-
publishCommandsFns[aggName] = sqsPublishJsons(makeQueueRef(queueUrl), "SQS_FIFO");
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return async (event, _context) => {
|
|
103
|
-
const taskActions = await handleEvents(event, _context);
|
|
104
|
-
|
|
105
|
-
await Promise.all(taskActions.map(async action => {
|
|
106
|
-
switch (action.TAG) {
|
|
107
|
-
case "PublishCommands": {
|
|
108
|
-
const aggregateName = action._0;
|
|
109
|
-
const cmdJsons = action._1;
|
|
110
|
-
const pub = publishCommandsFns[aggregateName];
|
|
111
|
-
if (pub !== undefined) {
|
|
112
|
-
await pub(cmdJsons);
|
|
113
|
-
} else {
|
|
114
|
-
log.warn(`no publish function for aggregate "${aggregateName}"`, { comp: "TaskBucketRuntime" });
|
|
115
|
-
}
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
case "CreateSchedule": {
|
|
119
|
-
const ops = makeSchedulerOps(config.scheduler);
|
|
120
|
-
if (ops) {
|
|
121
|
-
await ops.createSchedule(action._0);
|
|
122
|
-
} else {
|
|
123
|
-
log.warn("CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", { comp: "TaskBucketRuntime" });
|
|
124
|
-
}
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
case "DeleteSchedule": {
|
|
128
|
-
const ops = makeSchedulerOps(config.scheduler);
|
|
129
|
-
if (ops) {
|
|
130
|
-
await ops.deleteSchedule(action._0);
|
|
131
|
-
} else {
|
|
132
|
-
log.warn("DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", { comp: "TaskBucketRuntime" });
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
default:
|
|
137
|
-
log.warn(`unknown task action TAG ${action.TAG}`, { comp: "TaskBucketRuntime" });
|
|
138
|
-
}
|
|
139
|
-
}));
|
|
140
|
-
|
|
141
|
-
return "";
|
|
142
|
-
};
|
|
22
|
+
return Ops.makeHandler(callbackModule.callback, config);
|
|
143
23
|
}
|
|
144
24
|
|
|
145
25
|
const initPromise = buildHandler();
|
|
146
26
|
|
|
147
27
|
export async function handler(event, context) {
|
|
148
|
-
const
|
|
149
|
-
return await
|
|
28
|
+
const bucketHandler = await initPromise;
|
|
29
|
+
return await bucketHandler(event, context);
|
|
150
30
|
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// Typed cold-start core for the TaskBucket Lambda entry point.
|
|
2
|
+
//
|
|
3
|
+
// The "typed core, thin shell" split (docs/plans/minimize-lambda-entrypoint-mjs-shell.md):
|
|
4
|
+
// TaskBucketEntryPoint.mjs keeps only the inherently-untyped seam — the
|
|
5
|
+
// dynamic `import()` of the task callback module named in HANDLER_CONFIG and
|
|
6
|
+
// the read of its `callback` export. HANDLER_CONFIG parsing, the S3-event →
|
|
7
|
+
// task-action dispatch (an exhaustive match over Reventless.Task.taskAction —
|
|
8
|
+
// the former JS `switch (action.TAG)` with its unreachable "unknown TAG" arm), the
|
|
9
|
+
// per-aggregate command publishing, and the CloudWatch Events scheduler ops
|
|
10
|
+
// live here, fully type-checked.
|
|
11
|
+
//
|
|
12
|
+
// The former `.mjs` carried a hand-maintained mirror of
|
|
13
|
+
// `toScheduleExpression` ("kept in sync manually"); typed code calls
|
|
14
|
+
// ScheduledPublisher_CloudWatchEvents_Runtime directly — the layer ships it —
|
|
15
|
+
// so the mirror is gone.
|
|
16
|
+
|
|
17
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
18
|
+
@module("./HandlerFactoryHelpers.mjs") @scope("log")
|
|
19
|
+
external logWarn: (string, StreamRoutedEntryPoint_Ops.dispatchOpts) => unit = "warn"
|
|
20
|
+
|
|
21
|
+
// ── HANDLER_CONFIG ──────────────────────────────────────────────────────────
|
|
22
|
+
// Written by TaskRuntime_Builder_PerBucket.
|
|
23
|
+
|
|
24
|
+
type schedulerEnvConfig = {
|
|
25
|
+
roleArnEnv: string,
|
|
26
|
+
targetArnEnv: string,
|
|
27
|
+
targetNameEnv: string,
|
|
28
|
+
}
|
|
29
|
+
type handlerConfig = {
|
|
30
|
+
callbackModule: string,
|
|
31
|
+
// aggregateName → env-var name holding the aggregate's cmd-topic SQS URL.
|
|
32
|
+
publishToAggregates: dict<string>,
|
|
33
|
+
scheduler: option<schedulerEnvConfig>,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
|
|
37
|
+
obj->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
38
|
+
|
|
39
|
+
let parseHandlerConfig = (rawJson: string): handlerConfig => {
|
|
40
|
+
let obj =
|
|
41
|
+
(rawJson == "" ? "{}" : rawJson)
|
|
42
|
+
->JSON.parseOrThrow
|
|
43
|
+
->JSON.Decode.object
|
|
44
|
+
->Option.getOr(Dict.make())
|
|
45
|
+
{
|
|
46
|
+
callbackModule: obj->strOf("callbackModule")->Option.getOr(""),
|
|
47
|
+
publishToAggregates: obj
|
|
48
|
+
->Dict.get("publishToAggregates")
|
|
49
|
+
->Option.flatMap(JSON.Decode.object)
|
|
50
|
+
->Option.getOr(Dict.make())
|
|
51
|
+
->Dict.toArray
|
|
52
|
+
->Array.filterMap(((k, v)) => v->JSON.Decode.string->Option.map(s => (k, s)))
|
|
53
|
+
->Dict.fromArray,
|
|
54
|
+
scheduler: obj
|
|
55
|
+
->Dict.get("scheduler")
|
|
56
|
+
->Option.flatMap(JSON.Decode.object)
|
|
57
|
+
->Option.map(s => {
|
|
58
|
+
roleArnEnv: s->strOf("roleArnEnv")->Option.getOr(""),
|
|
59
|
+
targetArnEnv: s->strOf("targetArnEnv")->Option.getOr(""),
|
|
60
|
+
targetNameEnv: s->strOf("targetNameEnv")->Option.getOr(""),
|
|
61
|
+
}),
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Command publishing ──────────────────────────────────────────────────────
|
|
66
|
+
// aggregateName → publishJsons, resolving each queue URL via the env-var name
|
|
67
|
+
// carried in HANDLER_CONFIG; entries with an unset or empty URL are skipped
|
|
68
|
+
// (same as the former JS `if (queueUrl !== "")`).
|
|
69
|
+
|
|
70
|
+
let buildPublishCommands = (map: dict<string>): dict<ReventlessCore.CommandTopic.publishJsons> =>
|
|
71
|
+
map
|
|
72
|
+
->Dict.toArray
|
|
73
|
+
->Array.filterMap(((aggName, envVarName)) =>
|
|
74
|
+
processEnv
|
|
75
|
+
->Dict.get(envVarName)
|
|
76
|
+
->Option.filter(queueUrl => queueUrl != "")
|
|
77
|
+
->Option.map(queueUrl => {
|
|
78
|
+
let queue: Util_SQS_Runtime.resolvedQueue = {id: queueUrl, name: queueUrl, arn: ""}
|
|
79
|
+
(aggName, queue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS_FIFO))
|
|
80
|
+
})
|
|
81
|
+
)
|
|
82
|
+
->Dict.fromArray
|
|
83
|
+
|
|
84
|
+
// ── Scheduler ops (CloudWatch Events) ───────────────────────────────────────
|
|
85
|
+
// Resolved lazily on the first schedule action (tasks without schedules never
|
|
86
|
+
// touch the CloudWatch client) and cached for the container lifetime.
|
|
87
|
+
|
|
88
|
+
type schedulerOps = {
|
|
89
|
+
createSchedule: Reventless.Schedule.schedule => promise<unit>,
|
|
90
|
+
deleteSchedule: string => promise<unit>,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let schedulerOpsCache: ref<option<schedulerOps>> = ref(None)
|
|
94
|
+
|
|
95
|
+
let makeSchedulerOps = (config: option<schedulerEnvConfig>): option<schedulerOps> =>
|
|
96
|
+
switch (schedulerOpsCache.contents, config) {
|
|
97
|
+
| (Some(ops), _) => Some(ops)
|
|
98
|
+
| (None, None) => None
|
|
99
|
+
| (None, Some(cfg)) =>
|
|
100
|
+
switch (
|
|
101
|
+
processEnv->Dict.get(cfg.roleArnEnv),
|
|
102
|
+
processEnv->Dict.get(cfg.targetArnEnv),
|
|
103
|
+
processEnv->Dict.get(cfg.targetNameEnv),
|
|
104
|
+
) {
|
|
105
|
+
| (Some(roleArn), Some(targetArn), Some(targetName))
|
|
106
|
+
if roleArn != "" && targetArn != "" && targetName != "" =>
|
|
107
|
+
// The single resolved resource the shared CloudWatch Events runtime
|
|
108
|
+
// targets: `urn` is the PutTargets ARN, `name` the target id.
|
|
109
|
+
let resources: array<ReventlessInfra.Adapter.resolvedResource> = [
|
|
110
|
+
{
|
|
111
|
+
name: targetName,
|
|
112
|
+
id: targetName,
|
|
113
|
+
urn: targetArn,
|
|
114
|
+
service: "unknown",
|
|
115
|
+
resourceInfo: ReventlessInfra.Adapter.NoInfo,
|
|
116
|
+
role: "",
|
|
117
|
+
region: "",
|
|
118
|
+
resourceType: "",
|
|
119
|
+
configuration: Dict.make(),
|
|
120
|
+
tags: Dict.make(),
|
|
121
|
+
},
|
|
122
|
+
]
|
|
123
|
+
let ops = {
|
|
124
|
+
createSchedule: schedule =>
|
|
125
|
+
ScheduledPublisher_CloudWatchEvents_Runtime.createSchedule(~roleArn)(resources, schedule),
|
|
126
|
+
deleteSchedule: name =>
|
|
127
|
+
ScheduledPublisher_CloudWatchEvents_Runtime.deleteSchedule(resources, name),
|
|
128
|
+
}
|
|
129
|
+
schedulerOpsCache := Some(ops)
|
|
130
|
+
Some(ops)
|
|
131
|
+
| _ =>
|
|
132
|
+
logWarn("scheduler env vars missing — schedules will no-op", {comp: "TaskBucketRuntime"})
|
|
133
|
+
None
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Task-action dispatch ────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
let dispatchTaskActions = async (
|
|
140
|
+
~actions: array<Reventless.Task.taskAction>,
|
|
141
|
+
~publishCommands: dict<ReventlessCore.CommandTopic.publishJsons>,
|
|
142
|
+
~schedulerOps: unit => option<schedulerOps>,
|
|
143
|
+
): unit => {
|
|
144
|
+
let _ =
|
|
145
|
+
await actions
|
|
146
|
+
->Array.map(async action =>
|
|
147
|
+
switch action {
|
|
148
|
+
| Reventless.Task.PublishCommands(aggregateName, cmdJsons) =>
|
|
149
|
+
switch publishCommands->Dict.get(aggregateName) {
|
|
150
|
+
| Some(publish) => await publish(cmdJsons)
|
|
151
|
+
| None =>
|
|
152
|
+
logWarn(
|
|
153
|
+
`no publish function for aggregate "${aggregateName}"`,
|
|
154
|
+
{comp: "TaskBucketRuntime"},
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
| CreateSchedule(schedule) =>
|
|
158
|
+
switch schedulerOps() {
|
|
159
|
+
| Some(ops) => await ops.createSchedule(schedule)
|
|
160
|
+
| None =>
|
|
161
|
+
logWarn(
|
|
162
|
+
"CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
|
|
163
|
+
{comp: "TaskBucketRuntime"},
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
| DeleteSchedule(name) =>
|
|
167
|
+
switch schedulerOps() {
|
|
168
|
+
| Some(ops) => await ops.deleteSchedule(name)
|
|
169
|
+
| None =>
|
|
170
|
+
logWarn(
|
|
171
|
+
"DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
|
|
172
|
+
{comp: "TaskBucketRuntime"},
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
->Promise.all
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// ── Exported Lambda handler ─────────────────────────────────────────────────
|
|
181
|
+
// The shell reads `callback` off the dynamically imported task module and
|
|
182
|
+
// hands it here; from this point everything is typed
|
|
183
|
+
// (TaskBucket_S3_Runtime.handleBucketEvent decodes the S3 records and invokes
|
|
184
|
+
// the callback per object).
|
|
185
|
+
|
|
186
|
+
let makeHandler = (~callback: ReventlessCore.Task.bucketCallback, ~config: handlerConfig) => {
|
|
187
|
+
let handleEvents = TaskBucket_S3_Runtime.handleBucketEvent(callback)
|
|
188
|
+
let publishCommands = buildPublishCommands(config.publishToAggregates)
|
|
189
|
+
async (event: PulumiAws.S3.Bucket.event, context: PulumiAws.Lambda.context) => {
|
|
190
|
+
let actions = await handleEvents(event, context)
|
|
191
|
+
await dispatchTaskActions(
|
|
192
|
+
~actions,
|
|
193
|
+
~publishCommands,
|
|
194
|
+
~schedulerOps=() => makeSchedulerOps(config.scheduler),
|
|
195
|
+
)
|
|
196
|
+
""
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
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_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as HandlerFactoryHelpersMjs from "./HandlerFactoryHelpers.mjs";
|
|
7
|
+
import * as TaskBucket_S3_Runtime$ReventlessAws from "../Task/TaskBucket_S3_Runtime.res.mjs";
|
|
8
|
+
import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
9
|
+
import * as ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws from "../ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs";
|
|
10
|
+
|
|
11
|
+
function logWarn(prim0, prim1) {
|
|
12
|
+
HandlerFactoryHelpersMjs.log.warn(prim0, prim1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function strOf(obj, key) {
|
|
16
|
+
return Stdlib_Option.flatMap(obj[key], Stdlib_JSON.Decode.string);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseHandlerConfig(rawJson) {
|
|
20
|
+
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(rawJson === "" ? "{}" : rawJson)), {});
|
|
21
|
+
return {
|
|
22
|
+
callbackModule: Stdlib_Option.getOr(strOf(obj, "callbackModule"), ""),
|
|
23
|
+
publishToAggregates: Object.fromEntries(Stdlib_Array.filterMap(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["publishToAggregates"], Stdlib_JSON.Decode.object), {})), param => {
|
|
24
|
+
let k = param[0];
|
|
25
|
+
return Stdlib_Option.map(Stdlib_JSON.Decode.string(param[1]), s => [
|
|
26
|
+
k,
|
|
27
|
+
s
|
|
28
|
+
]);
|
|
29
|
+
})),
|
|
30
|
+
scheduler: Stdlib_Option.map(Stdlib_Option.flatMap(obj["scheduler"], Stdlib_JSON.Decode.object), s => ({
|
|
31
|
+
roleArnEnv: Stdlib_Option.getOr(strOf(s, "roleArnEnv"), ""),
|
|
32
|
+
targetArnEnv: Stdlib_Option.getOr(strOf(s, "targetArnEnv"), ""),
|
|
33
|
+
targetNameEnv: Stdlib_Option.getOr(strOf(s, "targetNameEnv"), "")
|
|
34
|
+
}))
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildPublishCommands(map) {
|
|
39
|
+
return Object.fromEntries(Stdlib_Array.filterMap(Object.entries(map), param => {
|
|
40
|
+
let aggName = param[0];
|
|
41
|
+
return Stdlib_Option.map(Stdlib_Option.filter(process.env[param[1]], queueUrl => queueUrl !== ""), queueUrl => {
|
|
42
|
+
let queue = {
|
|
43
|
+
id: queueUrl,
|
|
44
|
+
name: queueUrl,
|
|
45
|
+
arn: ""
|
|
46
|
+
};
|
|
47
|
+
return [
|
|
48
|
+
aggName,
|
|
49
|
+
CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO")
|
|
50
|
+
];
|
|
51
|
+
});
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let schedulerOpsCache = {
|
|
56
|
+
contents: undefined
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
function makeSchedulerOps(config) {
|
|
60
|
+
let match = schedulerOpsCache.contents;
|
|
61
|
+
if (match !== undefined) {
|
|
62
|
+
return match;
|
|
63
|
+
}
|
|
64
|
+
if (config === undefined) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let match$1 = process.env[config.roleArnEnv];
|
|
68
|
+
let match$2 = process.env[config.targetArnEnv];
|
|
69
|
+
let match$3 = process.env[config.targetNameEnv];
|
|
70
|
+
if (match$1 !== undefined && match$2 !== undefined && match$3 !== undefined && match$1 !== "" && match$2 !== "" && match$3 !== "") {
|
|
71
|
+
let resources = [{
|
|
72
|
+
name: match$3,
|
|
73
|
+
id: match$3,
|
|
74
|
+
urn: match$2,
|
|
75
|
+
resourceInfo: "NoInfo",
|
|
76
|
+
service: "unknown",
|
|
77
|
+
role: "",
|
|
78
|
+
region: "",
|
|
79
|
+
resourceType: "",
|
|
80
|
+
configuration: {},
|
|
81
|
+
tags: {}
|
|
82
|
+
}];
|
|
83
|
+
let ops_createSchedule = schedule => ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws.createSchedule(match$1)(resources, schedule);
|
|
84
|
+
let ops_deleteSchedule = name => ScheduledPublisher_CloudWatchEvents_Runtime$ReventlessAws.deleteSchedule(resources, name);
|
|
85
|
+
let ops = {
|
|
86
|
+
createSchedule: ops_createSchedule,
|
|
87
|
+
deleteSchedule: ops_deleteSchedule
|
|
88
|
+
};
|
|
89
|
+
schedulerOpsCache.contents = ops;
|
|
90
|
+
return ops;
|
|
91
|
+
}
|
|
92
|
+
HandlerFactoryHelpersMjs.log.warn("scheduler env vars missing — schedules will no-op", {
|
|
93
|
+
comp: "TaskBucketRuntime"
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function dispatchTaskActions(actions, publishCommands, schedulerOps) {
|
|
98
|
+
await Promise.all(actions.map(async action => {
|
|
99
|
+
switch (action.TAG) {
|
|
100
|
+
case "PublishCommands" :
|
|
101
|
+
let aggregateName = action._0;
|
|
102
|
+
let publish = publishCommands[aggregateName];
|
|
103
|
+
if (publish !== undefined) {
|
|
104
|
+
return await publish(action._1);
|
|
105
|
+
} else {
|
|
106
|
+
HandlerFactoryHelpersMjs.log.warn(`no publish function for aggregate "` + aggregateName + `"`, {
|
|
107
|
+
comp: "TaskBucketRuntime"
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
case "CreateSchedule" :
|
|
112
|
+
let ops = schedulerOps();
|
|
113
|
+
if (ops !== undefined) {
|
|
114
|
+
return await ops.createSchedule(action._0);
|
|
115
|
+
} else {
|
|
116
|
+
HandlerFactoryHelpersMjs.log.warn("CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", {
|
|
117
|
+
comp: "TaskBucketRuntime"
|
|
118
|
+
});
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
case "DeleteSchedule" :
|
|
122
|
+
let ops$1 = schedulerOps();
|
|
123
|
+
if (ops$1 !== undefined) {
|
|
124
|
+
return await ops$1.deleteSchedule(action._0);
|
|
125
|
+
} else {
|
|
126
|
+
HandlerFactoryHelpersMjs.log.warn("DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)", {
|
|
127
|
+
comp: "TaskBucketRuntime"
|
|
128
|
+
});
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function makeHandler(callback, config) {
|
|
136
|
+
let handleEvents = TaskBucket_S3_Runtime$ReventlessAws.handleBucketEvent(callback);
|
|
137
|
+
let publishCommands = buildPublishCommands(config.publishToAggregates);
|
|
138
|
+
return async (event, context) => {
|
|
139
|
+
let actions = await handleEvents(event, context);
|
|
140
|
+
await dispatchTaskActions(actions, publishCommands, () => makeSchedulerOps(config.scheduler));
|
|
141
|
+
return "";
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export {
|
|
146
|
+
logWarn,
|
|
147
|
+
strOf,
|
|
148
|
+
parseHandlerConfig,
|
|
149
|
+
buildPublishCommands,
|
|
150
|
+
schedulerOpsCache,
|
|
151
|
+
makeSchedulerOps,
|
|
152
|
+
dispatchTaskActions,
|
|
153
|
+
makeHandler,
|
|
154
|
+
}
|
|
155
|
+
/* ./HandlerFactoryHelpers.mjs Not a pure module */
|