@reventlessdev/reventless-aws 3.0.0-alpha.232 → 3.0.0-alpha.233
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 +8 -8
- 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_Ops.res +11 -1
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +9 -3
- 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/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/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/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,138 +1,54 @@
|
|
|
1
|
-
// AutomationSlice Lambda entry point.
|
|
2
|
-
// At cold start: reads HANDLER_CONFIG, dynamically imports
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// AutomationSlice / OutboundTranslationSlice Lambda entry point.
|
|
2
|
+
// At cold start: reads HANDLER_CONFIG, dynamically imports each slice's spec +
|
|
3
|
+
// body modules, applies the curried callback functor, and routes stream events
|
|
4
|
+
// by source URN.
|
|
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 spec + body modules
|
|
9
|
+
// named in HANDLER_CONFIG and the curried functor applications consuming them
|
|
10
|
+
// (AutomationSlice_Callback.Make(Spec)(Automation) /
|
|
11
|
+
// OutboundTranslationSlice_Callback.Make(Spec)(Translation)). Everything else
|
|
12
|
+
// (HANDLER_CONFIG parsing, the phase-1/phase-2 pipelines, the TODO-list
|
|
13
|
+
// QueryDb sync, the routed dispatch boundary) lives type-checked in
|
|
14
|
+
// AutomationSliceEntryPoint_Ops.res / StreamRoutedEntryPoint_Ops.res.
|
|
5
15
|
|
|
6
|
-
import * as Effect from "effect/Effect";
|
|
7
|
-
import * as Stream from "effect/Stream";
|
|
8
|
-
import * as Chunk from "effect/Chunk";
|
|
9
|
-
import { parseJsonOrThrow } from "sury/src/S.res.mjs";
|
|
10
|
-
import {
|
|
11
|
-
makeTableRef,
|
|
12
|
-
makeQueueRef,
|
|
13
|
-
log,
|
|
14
|
-
runEffect,
|
|
15
|
-
setRequestId,
|
|
16
|
-
extractMetaField,
|
|
17
|
-
extractSentTimestamp,
|
|
18
|
-
extractRetryCount,
|
|
19
|
-
} from "./HandlerFactoryHelpers.mjs";
|
|
20
|
-
import { save as qdbSave } from "@reventlessdev/reventless-aws/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res.mjs";
|
|
21
|
-
import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
22
16
|
import { Make as automationSliceCallbackMake } from "@reventlessdev/reventless-core/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs";
|
|
23
|
-
import { handleStreamEvent } from "@reventlessdev/reventless-aws/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs";
|
|
24
17
|
import { Make as outboundTranslationSliceCallbackMake } from "@reventlessdev/reventless-core/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
|
|
18
|
+
import * as StreamOps from "./StreamRoutedEntryPoint_Ops.res.mjs";
|
|
19
|
+
import * as Ops from "./AutomationSliceEntryPoint_Ops.res.mjs";
|
|
25
20
|
|
|
26
21
|
const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
|
|
27
22
|
|
|
28
|
-
// Same comp the slice's own callback logs under (AutomationSlice_Callback /
|
|
29
|
-
// OutboundTranslationSlice_Callback), so a filter catches both the framework's
|
|
30
|
-
// lines and the application handler's.
|
|
31
|
-
const sliceComp = (callbackType, sliceName) =>
|
|
32
|
-
callbackType === "outbound"
|
|
33
|
-
? `OutboundTranslationSlice(${sliceName})`
|
|
34
|
-
: `AutomationSlice(${sliceName})`;
|
|
35
|
-
|
|
36
|
-
function groupBySource(records) {
|
|
37
|
-
const dict = {};
|
|
38
|
-
records.forEach(record => {
|
|
39
|
-
const arn = record.eventSourceARN;
|
|
40
|
-
const existing = dict[arn] || [];
|
|
41
|
-
dict[arn] = existing.concat([record]);
|
|
42
|
-
});
|
|
43
|
-
return dict;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function buildHandler(specModule, callbackMake, queryDbTableName, dcbQueueUrl) {
|
|
47
|
-
const callback = callbackMake(specModule);
|
|
48
|
-
const publishJsons = sqsPublishJsons(makeQueueRef(dcbQueueUrl), "SQS_FIFO");
|
|
49
|
-
const table = makeTableRef(queryDbTableName);
|
|
50
|
-
const rawSave = qdbSave(table);
|
|
51
|
-
|
|
52
|
-
const syncToQueryDb = async () => {
|
|
53
|
-
const items = Object.entries(callback.todoItems.contents);
|
|
54
|
-
await Promise.all(items.map(async entry => await rawSave(entry[0], entry[1], "Overwrite", undefined)));
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const eventSchema = specModule.consumedEventSchema;
|
|
58
|
-
|
|
59
|
-
const jsonEventsHandler = stream => Effect.flatMap(
|
|
60
|
-
Stream.runCollect(
|
|
61
|
-
Stream.flatMap(
|
|
62
|
-
Stream.mapEffect(stream, json => Effect.sync(() => {
|
|
63
|
-
try {
|
|
64
|
-
const eventJson = json.event != null ? json.event : json;
|
|
65
|
-
return [parseJsonOrThrow(eventJson, eventSchema)];
|
|
66
|
-
} catch (exn) {
|
|
67
|
-
log.error("failed to decode event", { comp: "AutomationSliceRuntime", detail: exn && exn.message ? exn.message : String(exn) });
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
})),
|
|
71
|
-
events => Stream.fromIterable(events)
|
|
72
|
-
)
|
|
73
|
-
),
|
|
74
|
-
chunk => Effect.promise(async () => {
|
|
75
|
-
const eventsArr = Chunk.toReadonlyArray(chunk);
|
|
76
|
-
callback.phase1(eventsArr);
|
|
77
|
-
await callback.phase2(publishJsons);
|
|
78
|
-
return await syncToQueryDb();
|
|
79
|
-
})
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
return (event, context) => handleStreamEvent(jsonEventsHandler, event, context);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
23
|
async function buildAllHandlers() {
|
|
86
|
-
const configStr = process.env["HANDLER_CONFIG"] || '{"handlers":[]}';
|
|
87
|
-
const config = JSON.parse(configStr);
|
|
88
24
|
const handlers = {};
|
|
89
|
-
|
|
90
|
-
const specModule = await dynamicImport(h.specModule);
|
|
91
|
-
const callbackMake = h.callbackType === "outbound"
|
|
92
|
-
? prim => outboundTranslationSliceCallbackMake(prim)
|
|
93
|
-
: prim => automationSliceCallbackMake(prim);
|
|
94
|
-
const handler = buildHandler(specModule, callbackMake, h.queryDbTableName, h.dcbQueueUrl);
|
|
95
|
-
// Multiple slices can share one source stream (e.g. several automation
|
|
96
|
-
// slices reacting to the same DcbEventLog). Accumulate all handlers per
|
|
97
|
-
// source ARN; a plain assignment collapses them to one (Promise.all race),
|
|
98
|
-
// silently dropping the rest. Each keeps its own comp — that is what
|
|
99
|
-
// separates co-hosted slices in the log stream.
|
|
100
|
-
(handlers[h.sourceUrn] ||= []).push({
|
|
101
|
-
handler,
|
|
102
|
-
comp: sliceComp(h.callbackType, specModule.name),
|
|
103
|
-
});
|
|
104
|
-
}));
|
|
105
|
-
return handlers;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const initPromise = buildAllHandlers();
|
|
25
|
+
const entries = Ops.parseHandlerConfig(process.env["HANDLER_CONFIG"] || "");
|
|
109
26
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const grouped = groupBySource(records);
|
|
115
|
-
|
|
116
|
-
await Promise.all(Object.entries(grouped).map(async ([arn, subRecords]) => {
|
|
117
|
-
const streamHandlers = handlers[arn];
|
|
118
|
-
if (streamHandlers !== undefined && streamHandlers.length > 0) {
|
|
119
|
-
log.debug("found " + streamHandlers.length + " handler(s) for " + arn, { comp: "AutomationSliceRuntime" });
|
|
120
|
-
const correlationId = extractMetaField(subRecords, "correlationId");
|
|
121
|
-
const causationId = extractMetaField(subRecords, "causationId");
|
|
122
|
-
const timestamp = extractSentTimestamp(subRecords);
|
|
123
|
-
const retryCount = extractRetryCount(subRecords);
|
|
124
|
-
await Promise.all(streamHandlers.map(({ handler: h, comp }) =>
|
|
125
|
-
runEffect(h({ Records: subRecords }, context), {
|
|
126
|
-
correlationId,
|
|
127
|
-
causationId,
|
|
128
|
-
comp,
|
|
129
|
-
timestamp,
|
|
130
|
-
retryCount,
|
|
131
|
-
})
|
|
132
|
-
));
|
|
133
|
-
} else {
|
|
134
|
-
log.warn("no handler found: " + arn, { comp: "AutomationSliceRuntime" });
|
|
27
|
+
await Promise.all(entries.map(async (entry) => {
|
|
28
|
+
if (!entry.bodyModule) {
|
|
29
|
+
Ops.warnMissingBodyModule(entry);
|
|
30
|
+
return;
|
|
135
31
|
}
|
|
32
|
+
const [specModule, bodyModule] = await Promise.all([
|
|
33
|
+
dynamicImport(entry.specModule),
|
|
34
|
+
dynamicImport(entry.bodyModule),
|
|
35
|
+
]);
|
|
36
|
+
const registered = entry.callbackType === "outbound"
|
|
37
|
+
? Ops.makeOutboundRegisteredHandler(
|
|
38
|
+
entry,
|
|
39
|
+
specModule.name,
|
|
40
|
+
specModule.consumedEventSchema,
|
|
41
|
+
outboundTranslationSliceCallbackMake(specModule)(bodyModule)
|
|
42
|
+
)
|
|
43
|
+
: Ops.makeAutomationRegisteredHandler(
|
|
44
|
+
entry,
|
|
45
|
+
specModule.name,
|
|
46
|
+
automationSliceCallbackMake(specModule)(bodyModule)
|
|
47
|
+
);
|
|
48
|
+
StreamOps.addToRegistry(handlers, entry.sourceUrn, registered);
|
|
136
49
|
}));
|
|
137
|
-
|
|
50
|
+
|
|
51
|
+
return handlers;
|
|
138
52
|
}
|
|
53
|
+
|
|
54
|
+
export const handler = StreamOps.makeRoutedHandler("AutomationSliceRuntime", buildAllHandlers());
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// Typed cold-start core for the AutomationSlice / OutboundTranslationSlice
|
|
2
|
+
// Lambda entry point.
|
|
3
|
+
//
|
|
4
|
+
// The "typed core, thin shell" split (docs/plans/minimize-lambda-entrypoint-mjs-shell.md):
|
|
5
|
+
// AutomationSliceEntryPoint.mjs keeps only the inherently-untyped seam — the
|
|
6
|
+
// dynamic `import()` of the spec + body modules named in HANDLER_CONFIG and
|
|
7
|
+
// the curried callback-functor applications consuming them
|
|
8
|
+
// (AutomationSlice_Callback.Make(Spec)(Automation) /
|
|
9
|
+
// OutboundTranslationSlice_Callback.Make(Spec)(Translation)). HANDLER_CONFIG
|
|
10
|
+
// parsing, the two phase-1/phase-2 pipelines, the TODO-list QueryDb sync, and
|
|
11
|
+
// handler registration live here, fully type-checked; the routed dispatch
|
|
12
|
+
// boundary is shared with the ReadModel / StateViewSlice entry points in
|
|
13
|
+
// StreamRoutedEntryPoint_Ops.
|
|
14
|
+
//
|
|
15
|
+
// This conversion also REPAIRS the wiring: the former `.mjs` predated the
|
|
16
|
+
// mixed-source callback rework — it applied the (now curried) functor to the
|
|
17
|
+
// spec module alone, read `todoItems.contents` (no longer a ref), and passed
|
|
18
|
+
// schema-decoded events to `phase1` (the automation callback now takes raw
|
|
19
|
+
// envelope JSONs plus a context). Any event through that Lambda threw at
|
|
20
|
+
// `callback.phase1`. The `bodyModule` and `context` HANDLER_CONFIG fields
|
|
21
|
+
// exist for this wiring (AutomationSliceRuntime_Builder_Single).
|
|
22
|
+
|
|
23
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
24
|
+
|
|
25
|
+
type handlerEntry = {
|
|
26
|
+
specModule: string,
|
|
27
|
+
bodyModule: string,
|
|
28
|
+
callbackType: string,
|
|
29
|
+
queryDbTableName: string,
|
|
30
|
+
dcbQueueUrl: string,
|
|
31
|
+
sourceUrn: string,
|
|
32
|
+
context: option<Reventless.AutomationSlice.context>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
|
|
36
|
+
obj->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
37
|
+
|
|
38
|
+
let decodeContext = (json: JSON.t): option<Reventless.AutomationSlice.context> =>
|
|
39
|
+
json
|
|
40
|
+
->JSON.Decode.object
|
|
41
|
+
->Option.map(c => {
|
|
42
|
+
Reventless.AutomationSlice.environment: c->strOf("environment")->Option.getOr("unknown"),
|
|
43
|
+
platformName: c->strOf("platformName")->Option.getOr("unknown"),
|
|
44
|
+
pluginName: c->strOf("pluginName")->Option.getOr("unknown"),
|
|
45
|
+
sliceName: c->strOf("sliceName")->Option.getOr("unknown"),
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
let decodeEntry = (json: JSON.t): option<handlerEntry> =>
|
|
49
|
+
json
|
|
50
|
+
->JSON.Decode.object
|
|
51
|
+
->Option.map(h => {
|
|
52
|
+
specModule: h->strOf("specModule")->Option.getOr(""),
|
|
53
|
+
bodyModule: h->strOf("bodyModule")->Option.getOr(""),
|
|
54
|
+
callbackType: h->strOf("callbackType")->Option.getOr("automation"),
|
|
55
|
+
queryDbTableName: h->strOf("queryDbTableName")->Option.getOr(""),
|
|
56
|
+
dcbQueueUrl: h->strOf("dcbQueueUrl")->Option.getOr(""),
|
|
57
|
+
sourceUrn: h->strOf("sourceUrn")->Option.getOr(""),
|
|
58
|
+
context: h->Dict.get("context")->Option.flatMap(decodeContext),
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
let parseHandlerConfig = (rawJson: string): array<handlerEntry> =>
|
|
62
|
+
rawJson == ""
|
|
63
|
+
? []
|
|
64
|
+
: rawJson
|
|
65
|
+
->JSON.parseOrThrow
|
|
66
|
+
->JSON.Decode.object
|
|
67
|
+
->Option.flatMap(obj => obj->Dict.get("handlers"))
|
|
68
|
+
->Option.flatMap(JSON.Decode.array)
|
|
69
|
+
->Option.getOr([])
|
|
70
|
+
->Array.filterMap(decodeEntry)
|
|
71
|
+
|
|
72
|
+
// A pre-`bodyModule` config entry cannot rebuild the callback (the spec module
|
|
73
|
+
// alone lacks the mappings/process). Warn and skip rather than crash the whole
|
|
74
|
+
// Lambda — co-hosted slices with complete entries still run.
|
|
75
|
+
let warnMissingBodyModule = (entry: handlerEntry) =>
|
|
76
|
+
StreamRoutedEntryPoint_Ops.logWarn(
|
|
77
|
+
`handler entry for spec '${entry.specModule}' carries no bodyModule (stale HANDLER_CONFIG?); skipping`,
|
|
78
|
+
{comp: "AutomationSliceRuntime"},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
// Fallback for automation entries from a config without `context` — sliceName
|
|
82
|
+
// is the only field the runtime can still recover.
|
|
83
|
+
let defaultContext = (sliceName: string): Reventless.AutomationSlice.context => {
|
|
84
|
+
environment: processEnv->Dict.get("Environment")->Option.getOr("unknown"),
|
|
85
|
+
platformName: "unknown",
|
|
86
|
+
pluginName: "unknown",
|
|
87
|
+
sliceName,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ── Shared plumbing ─────────────────────────────────────────────────────────
|
|
91
|
+
|
|
92
|
+
let makePublishJsons = (queueUrl: string): ReventlessCore.CommandTopic.publishJsons => {
|
|
93
|
+
let queue: Util_SQS_Runtime.resolvedQueue = {id: queueUrl, name: queueUrl, arn: ""}
|
|
94
|
+
queue->CommandTopicChannel_SQS_Runtime.publishJsons(AWS.SQS_FIFO)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Overwrite-sync the in-memory TODO list into the slice's view table. Rows are
|
|
98
|
+
// serialized through the callback's row schema — the same JSON shape the
|
|
99
|
+
// deploy-time QueryDb writes.
|
|
100
|
+
let makeSyncTodoItems = (
|
|
101
|
+
~queryDbTableName: string,
|
|
102
|
+
~todoItems: dict<'row>,
|
|
103
|
+
~rowSchema: S.t<'row>,
|
|
104
|
+
): (unit => promise<unit>) => {
|
|
105
|
+
let table: Util_DynamoDb_Runtime.resolvedTable = {
|
|
106
|
+
id: "",
|
|
107
|
+
name: queryDbTableName,
|
|
108
|
+
arn: "",
|
|
109
|
+
hashKey: "id",
|
|
110
|
+
}
|
|
111
|
+
let save = QueryDbStorage_DynamoDb_Runtime.save(table)
|
|
112
|
+
async () => {
|
|
113
|
+
let _ =
|
|
114
|
+
await todoItems
|
|
115
|
+
->Dict.toArray
|
|
116
|
+
->Array.map(((id, row)) =>
|
|
117
|
+
save(id, row->S.reverseConvertToJsonOrThrow(rowSchema), ReventlessCore.QueryDb.Overwrite, None)
|
|
118
|
+
)
|
|
119
|
+
->Promise.all
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Phase-1/phase-2 pipelines ───────────────────────────────────────────────
|
|
124
|
+
// Both run: collect the batch → phase 1 → sync (so consumers observe Pending
|
|
125
|
+
// rows even if phase 2 fails) → phase 2 → sync. Unlike the in-process builders
|
|
126
|
+
// (which DETACH phase 2 to avoid a local-bus self-deadlock), the Lambda AWAITS
|
|
127
|
+
// it: SQS decouples the downstream fan-out, and a detached promise would
|
|
128
|
+
// freeze with the runtime between invocations.
|
|
129
|
+
|
|
130
|
+
// The callback shapes the shell's functor applications produce
|
|
131
|
+
// (AutomationSlice_Callback.T / OutboundTranslationSlice_Callback.T), typed at
|
|
132
|
+
// the seam.
|
|
133
|
+
type automationCallback = {
|
|
134
|
+
todoItems: dict<ReventlessCore.AutomationSlice_Callback.todoRow>,
|
|
135
|
+
phase1: (array<JSON.t>, Reventless.AutomationSlice.context) => unit,
|
|
136
|
+
phase2: ReventlessCore.CommandTopic.publishJsons => promise<unit>,
|
|
137
|
+
}
|
|
138
|
+
type outboundCallback<'event> = {
|
|
139
|
+
todoItems: dict<ReventlessCore.OutboundTranslationSlice_Callback.todoRow>,
|
|
140
|
+
phase1: array<'event> => unit,
|
|
141
|
+
phase2: ReventlessCore.CommandTopic.publishJsons => promise<unit>,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Automation: phase 1 takes the RAW envelope JSONs — the callback routes by
|
|
145
|
+
// `meta.service` and unwraps `{id, meta, event}` itself — plus the context.
|
|
146
|
+
let makeAutomationJsonEventsHandler = (
|
|
147
|
+
~context: Reventless.AutomationSlice.context,
|
|
148
|
+
~callback: automationCallback,
|
|
149
|
+
~publishJsons: ReventlessCore.CommandTopic.publishJsons,
|
|
150
|
+
~syncTodoItems: unit => promise<unit>,
|
|
151
|
+
): ReventlessCore.EventCollector.jsonEventsHandler =>
|
|
152
|
+
stream =>
|
|
153
|
+
stream
|
|
154
|
+
->Stream.runCollect
|
|
155
|
+
->Effect.flatMap(jsons =>
|
|
156
|
+
Effect.promise(async () => {
|
|
157
|
+
callback.phase1(jsons, context)
|
|
158
|
+
await syncTodoItems()
|
|
159
|
+
await callback.phase2(publishJsons)
|
|
160
|
+
await syncTodoItems()
|
|
161
|
+
})
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
// Outbound: phase 1 takes DECODED consumed events. Mirrors the in-process
|
|
165
|
+
// builder's decode — the tolerant DcbDecode on the inner `event` payload, so
|
|
166
|
+
// event types the slice does not consume are dropped silently instead of
|
|
167
|
+
// logging an error per event.
|
|
168
|
+
let makeOutboundJsonEventsHandler = (
|
|
169
|
+
~consumedEventSchema: S.t<'event>,
|
|
170
|
+
~callback: outboundCallback<'event>,
|
|
171
|
+
~publishJsons: ReventlessCore.CommandTopic.publishJsons,
|
|
172
|
+
~syncTodoItems: unit => promise<unit>,
|
|
173
|
+
): ReventlessCore.EventCollector.jsonEventsHandler => {
|
|
174
|
+
let decoder = Reventless.DcbDecode.makeDecoder(consumedEventSchema)
|
|
175
|
+
stream =>
|
|
176
|
+
stream
|
|
177
|
+
->Stream.mapEffect(json =>
|
|
178
|
+
Effect.sync(() => {
|
|
179
|
+
let rawEvent =
|
|
180
|
+
json
|
|
181
|
+
->JSON.Decode.object
|
|
182
|
+
->Option.flatMap(d => d->Dict.get("event"))
|
|
183
|
+
->Option.getOr(json)
|
|
184
|
+
let (eventType, dataDict) = rawEvent->ReventlessCore.Message.splitMessage
|
|
185
|
+
switch decoder.decode(~eventType, ~data=dataDict) {
|
|
186
|
+
| Some(event) => [event]
|
|
187
|
+
| None => []
|
|
188
|
+
}
|
|
189
|
+
})
|
|
190
|
+
)
|
|
191
|
+
->Stream.flatMap(events => Stream.fromIterable(events))
|
|
192
|
+
->Stream.runCollect
|
|
193
|
+
->Effect.flatMap(events =>
|
|
194
|
+
Effect.promise(async () => {
|
|
195
|
+
callback.phase1(events)
|
|
196
|
+
await syncTodoItems()
|
|
197
|
+
await callback.phase2(publishJsons)
|
|
198
|
+
await syncTodoItems()
|
|
199
|
+
})
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── Handler assembly (called by the shell per HANDLER_CONFIG entry) ─────────
|
|
204
|
+
// Comp matches what the slice's own callback logs under, so a filter catches
|
|
205
|
+
// both the framework's lines and the application handler's.
|
|
206
|
+
|
|
207
|
+
let makeAutomationRegisteredHandler = (
|
|
208
|
+
entry: handlerEntry,
|
|
209
|
+
~sliceName: string,
|
|
210
|
+
~callback: automationCallback,
|
|
211
|
+
): StreamRoutedEntryPoint_Ops.registeredHandler => {
|
|
212
|
+
handler: StreamRoutedEntryPoint_Ops.toStreamHandler(
|
|
213
|
+
makeAutomationJsonEventsHandler(
|
|
214
|
+
~context=entry.context->Option.getOr(defaultContext(sliceName)),
|
|
215
|
+
~callback,
|
|
216
|
+
~publishJsons=makePublishJsons(entry.dcbQueueUrl),
|
|
217
|
+
~syncTodoItems=makeSyncTodoItems(
|
|
218
|
+
~queryDbTableName=entry.queryDbTableName,
|
|
219
|
+
~todoItems=callback.todoItems,
|
|
220
|
+
~rowSchema=ReventlessCore.AutomationSlice_Callback.todoRowSchema,
|
|
221
|
+
),
|
|
222
|
+
),
|
|
223
|
+
),
|
|
224
|
+
comp: `AutomationSlice(${sliceName})`,
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
let makeOutboundRegisteredHandler = (
|
|
228
|
+
entry: handlerEntry,
|
|
229
|
+
~sliceName: string,
|
|
230
|
+
~consumedEventSchema: S.t<'event>,
|
|
231
|
+
~callback: outboundCallback<'event>,
|
|
232
|
+
): StreamRoutedEntryPoint_Ops.registeredHandler => {
|
|
233
|
+
handler: StreamRoutedEntryPoint_Ops.toStreamHandler(
|
|
234
|
+
makeOutboundJsonEventsHandler(
|
|
235
|
+
~consumedEventSchema,
|
|
236
|
+
~callback,
|
|
237
|
+
~publishJsons=makePublishJsons(entry.dcbQueueUrl),
|
|
238
|
+
~syncTodoItems=makeSyncTodoItems(
|
|
239
|
+
~queryDbTableName=entry.queryDbTableName,
|
|
240
|
+
~todoItems=callback.todoItems,
|
|
241
|
+
~rowSchema=ReventlessCore.OutboundTranslationSlice_Callback.todoRowSchema,
|
|
242
|
+
),
|
|
243
|
+
),
|
|
244
|
+
),
|
|
245
|
+
comp: `OutboundTranslationSlice(${sliceName})`,
|
|
246
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
5
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
6
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
7
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Stream$1 from "effect/Stream";
|
|
10
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
11
|
+
import * as DcbDecode$Reventless from "@reventlessdev/reventless-spec/src/components/DcbDecode.res.mjs";
|
|
12
|
+
import * as Message$ReventlessCore from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
13
|
+
import * as AutomationSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/AutomationSlice/AutomationSlice_Callback.res.mjs";
|
|
14
|
+
import * as StreamRoutedEntryPoint_Ops$ReventlessAws from "./StreamRoutedEntryPoint_Ops.res.mjs";
|
|
15
|
+
import * as CommandTopicChannel_SQS_Runtime$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
16
|
+
import * as QueryDbStorage_DynamoDb_Runtime$ReventlessAws from "../QueryDb/QueryDbStorage_DynamoDb_Runtime.res.mjs";
|
|
17
|
+
import * as OutboundTranslationSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
|
|
18
|
+
|
|
19
|
+
function strOf(obj, key) {
|
|
20
|
+
return Stdlib_Option.flatMap(obj[key], Stdlib_JSON.Decode.string);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function decodeContext(json) {
|
|
24
|
+
return Stdlib_Option.map(Stdlib_JSON.Decode.object(json), c => ({
|
|
25
|
+
environment: Stdlib_Option.getOr(strOf(c, "environment"), "unknown"),
|
|
26
|
+
platformName: Stdlib_Option.getOr(strOf(c, "platformName"), "unknown"),
|
|
27
|
+
pluginName: Stdlib_Option.getOr(strOf(c, "pluginName"), "unknown"),
|
|
28
|
+
sliceName: Stdlib_Option.getOr(strOf(c, "sliceName"), "unknown")
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function decodeEntry(json) {
|
|
33
|
+
return Stdlib_Option.map(Stdlib_JSON.Decode.object(json), h => ({
|
|
34
|
+
specModule: Stdlib_Option.getOr(strOf(h, "specModule"), ""),
|
|
35
|
+
bodyModule: Stdlib_Option.getOr(strOf(h, "bodyModule"), ""),
|
|
36
|
+
callbackType: Stdlib_Option.getOr(strOf(h, "callbackType"), "automation"),
|
|
37
|
+
queryDbTableName: Stdlib_Option.getOr(strOf(h, "queryDbTableName"), ""),
|
|
38
|
+
dcbQueueUrl: Stdlib_Option.getOr(strOf(h, "dcbQueueUrl"), ""),
|
|
39
|
+
sourceUrn: Stdlib_Option.getOr(strOf(h, "sourceUrn"), ""),
|
|
40
|
+
context: Stdlib_Option.flatMap(h["context"], decodeContext)
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function parseHandlerConfig(rawJson) {
|
|
45
|
+
if (rawJson === "") {
|
|
46
|
+
return [];
|
|
47
|
+
} else {
|
|
48
|
+
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);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function warnMissingBodyModule(entry) {
|
|
53
|
+
StreamRoutedEntryPoint_Ops$ReventlessAws.logWarn(`handler entry for spec '` + entry.specModule + `' carries no bodyModule (stale HANDLER_CONFIG?); skipping`, {
|
|
54
|
+
comp: "AutomationSliceRuntime"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function defaultContext(sliceName) {
|
|
59
|
+
return {
|
|
60
|
+
environment: Stdlib_Option.getOr(process.env["Environment"], "unknown"),
|
|
61
|
+
platformName: "unknown",
|
|
62
|
+
pluginName: "unknown",
|
|
63
|
+
sliceName: sliceName
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function makePublishJsons(queueUrl) {
|
|
68
|
+
let queue = {
|
|
69
|
+
id: queueUrl,
|
|
70
|
+
name: queueUrl,
|
|
71
|
+
arn: ""
|
|
72
|
+
};
|
|
73
|
+
return CommandTopicChannel_SQS_Runtime$ReventlessAws.publishJsons(queue, "SQS_FIFO");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function makeSyncTodoItems(queryDbTableName, todoItems, rowSchema) {
|
|
77
|
+
let table = {
|
|
78
|
+
id: "",
|
|
79
|
+
name: queryDbTableName,
|
|
80
|
+
arn: "",
|
|
81
|
+
hashKey: "id"
|
|
82
|
+
};
|
|
83
|
+
let save = QueryDbStorage_DynamoDb_Runtime$ReventlessAws.save(table);
|
|
84
|
+
return async () => {
|
|
85
|
+
await Promise.all(Object.entries(todoItems).map(param => save(param[0], S.reverseConvertToJsonOrThrow(param[1], rowSchema), "Overwrite", undefined)));
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function makeAutomationJsonEventsHandler(context, callback, publishJsons, syncTodoItems) {
|
|
90
|
+
return stream => Effect.flatMap(Stream.runCollect(stream), jsons => Effect.promise(async () => {
|
|
91
|
+
callback.phase1(jsons, context);
|
|
92
|
+
await syncTodoItems();
|
|
93
|
+
await callback.phase2(publishJsons);
|
|
94
|
+
return await syncTodoItems();
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function makeOutboundJsonEventsHandler(consumedEventSchema, callback, publishJsons, syncTodoItems) {
|
|
99
|
+
let decoder = DcbDecode$Reventless.makeDecoder(consumedEventSchema);
|
|
100
|
+
return stream => Effect.flatMap(Stream.runCollect(Stream$1.flatMap(Stream$1.mapEffect(stream, json => Effect.sync(() => {
|
|
101
|
+
let rawEvent = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), d => d["event"]), json);
|
|
102
|
+
let match = Message$ReventlessCore.splitMessage(rawEvent);
|
|
103
|
+
let event = decoder.decode(match[0], match[1]);
|
|
104
|
+
if (event !== undefined) {
|
|
105
|
+
return [Primitive_option.valFromOption(event)];
|
|
106
|
+
} else {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
})), events => Stream$1.fromIterable(events))), events => Effect.promise(async () => {
|
|
110
|
+
callback.phase1(events);
|
|
111
|
+
await syncTodoItems();
|
|
112
|
+
await callback.phase2(publishJsons);
|
|
113
|
+
return await syncTodoItems();
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function makeAutomationRegisteredHandler(entry, sliceName, callback) {
|
|
118
|
+
return {
|
|
119
|
+
handler: StreamRoutedEntryPoint_Ops$ReventlessAws.toStreamHandler(makeAutomationJsonEventsHandler(Stdlib_Option.getOr(entry.context, defaultContext(sliceName)), callback, makePublishJsons(entry.dcbQueueUrl), makeSyncTodoItems(entry.queryDbTableName, callback.todoItems, AutomationSlice_Callback$ReventlessCore.todoRowSchema))),
|
|
120
|
+
comp: `AutomationSlice(` + sliceName + `)`
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function makeOutboundRegisteredHandler(entry, sliceName, consumedEventSchema, callback) {
|
|
125
|
+
return {
|
|
126
|
+
handler: StreamRoutedEntryPoint_Ops$ReventlessAws.toStreamHandler(makeOutboundJsonEventsHandler(consumedEventSchema, callback, makePublishJsons(entry.dcbQueueUrl), makeSyncTodoItems(entry.queryDbTableName, callback.todoItems, OutboundTranslationSlice_Callback$ReventlessCore.todoRowSchema))),
|
|
127
|
+
comp: `OutboundTranslationSlice(` + sliceName + `)`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
strOf,
|
|
133
|
+
decodeContext,
|
|
134
|
+
decodeEntry,
|
|
135
|
+
parseHandlerConfig,
|
|
136
|
+
warnMissingBodyModule,
|
|
137
|
+
defaultContext,
|
|
138
|
+
makePublishJsons,
|
|
139
|
+
makeSyncTodoItems,
|
|
140
|
+
makeAutomationJsonEventsHandler,
|
|
141
|
+
makeOutboundJsonEventsHandler,
|
|
142
|
+
makeAutomationRegisteredHandler,
|
|
143
|
+
makeOutboundRegisteredHandler,
|
|
144
|
+
}
|
|
145
|
+
/* S Not a pure module */
|
|
@@ -8,8 +8,17 @@ type runtimeParts = Util.Lambda.runtimeParts
|
|
|
8
8
|
|
|
9
9
|
type sliceInfo = {
|
|
10
10
|
specModulePath: string,
|
|
11
|
+
// The slice's body module (`<Slice>_Automation.res` / `<Slice>_Translation.res`)
|
|
12
|
+
// — the second argument of the curried callback functor
|
|
13
|
+
// (AutomationSlice_Callback.Make(Spec)(Automation)). The entry point must
|
|
14
|
+
// import it alongside the spec; a spec module alone cannot rebuild the
|
|
15
|
+
// callback.
|
|
16
|
+
bodyModulePath: string,
|
|
11
17
|
callbackType: string,
|
|
12
18
|
queryDbTableName: Pulumi.Output.t<string>,
|
|
19
|
+
// Threaded to phase 1 collect/resolve on the automation path (mirrors the
|
|
20
|
+
// in-process builder's `~context`); outbound slices take no context.
|
|
21
|
+
context: option<Reventless.AutomationSlice.context>,
|
|
13
22
|
}
|
|
14
23
|
|
|
15
24
|
let bundledInfos: dict<sliceInfo> = Dict.make()
|
|
@@ -24,10 +33,12 @@ let getDcbQueueUrl = () => dcbQueueUrlRef.contents
|
|
|
24
33
|
let registerAutomationSlice = (
|
|
25
34
|
~name,
|
|
26
35
|
~specModulePath,
|
|
36
|
+
~bodyModulePath,
|
|
27
37
|
~callbackType="automation",
|
|
28
38
|
~queryDbTableName,
|
|
39
|
+
~context=?,
|
|
29
40
|
) =>
|
|
30
|
-
bundledInfos->Dict.set(name, {specModulePath, callbackType, queryDbTableName})
|
|
41
|
+
bundledInfos->Dict.set(name, {specModulePath, bodyModulePath, callbackType, queryDbTableName, context})
|
|
31
42
|
|
|
32
43
|
type storedSpec = {
|
|
33
44
|
componentName: string,
|
|
@@ -128,17 +139,31 @@ let finish = () =>
|
|
|
128
139
|
| Some(info) =>
|
|
129
140
|
let pkg = Util_Bundle.extractPackageName(info.specModulePath)
|
|
130
141
|
packageDirs->Dict.set(pkg, Util_Bundle.resolvePackageRoot(pkg))
|
|
142
|
+
// Usually the same package as the spec, but bundle it explicitly —
|
|
143
|
+
// the entry point imports the body module by specifier too.
|
|
144
|
+
let bodyPkg = Util_Bundle.extractPackageName(info.bodyModulePath)
|
|
145
|
+
packageDirs->Dict.set(bodyPkg, Util_Bundle.resolvePackageRoot(bodyPkg))
|
|
131
146
|
|
|
132
147
|
let specModule =
|
|
133
148
|
info.specModulePath->JSON.stringifyAny->Option.getOr(`""`)
|
|
149
|
+
let bodyModule =
|
|
150
|
+
info.bodyModulePath->JSON.stringifyAny->Option.getOr(`""`)
|
|
134
151
|
let callbackType =
|
|
135
152
|
info.callbackType->JSON.stringifyAny->Option.getOr(`""`)
|
|
153
|
+
let contextFragment = switch info.context {
|
|
154
|
+
| Some(context) =>
|
|
155
|
+
context
|
|
156
|
+
->JSON.stringifyAny
|
|
157
|
+
->Option.map(json => `,"context":${json}`)
|
|
158
|
+
->Option.getOr("")
|
|
159
|
+
| None => ""
|
|
160
|
+
}
|
|
136
161
|
|
|
137
162
|
let handlerJson =
|
|
138
163
|
Pulumi.Output.all3((info.queryDbTableName, dcbQueueUrl, spec.sourceUrns))
|
|
139
164
|
->Pulumi.Output.apply(((tableName, queueUrl, urns)) => {
|
|
140
165
|
let sourceUrn = urns->Array.getUnsafe(0)
|
|
141
|
-
`{"specModule":${specModule},"callbackType":${callbackType},"queryDbTableName":"${tableName}","dcbQueueUrl":"${queueUrl}","sourceUrn":"${sourceUrn}"}`
|
|
166
|
+
`{"specModule":${specModule},"bodyModule":${bodyModule},"callbackType":${callbackType},"queryDbTableName":"${tableName}","dcbQueueUrl":"${queueUrl}","sourceUrn":"${sourceUrn}"${contextFragment}}`
|
|
142
167
|
})
|
|
143
168
|
let _ = handlerOutputs->Array.push(handlerJson)
|
|
144
169
|
| None =>
|
|
@@ -26,12 +26,14 @@ function getDcbQueueUrl() {
|
|
|
26
26
|
return dcbQueueUrlRef.contents;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function registerAutomationSlice(name, specModulePath, callbackTypeOpt, queryDbTableName) {
|
|
29
|
+
function registerAutomationSlice(name, specModulePath, bodyModulePath, callbackTypeOpt, queryDbTableName, context) {
|
|
30
30
|
let callbackType = callbackTypeOpt !== undefined ? callbackTypeOpt : "automation";
|
|
31
31
|
bundledInfos[name] = {
|
|
32
32
|
specModulePath: specModulePath,
|
|
33
|
+
bodyModulePath: bodyModulePath,
|
|
33
34
|
callbackType: callbackType,
|
|
34
|
-
queryDbTableName: queryDbTableName
|
|
35
|
+
queryDbTableName: queryDbTableName,
|
|
36
|
+
context: context
|
|
35
37
|
};
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -104,15 +106,20 @@ function finish() {
|
|
|
104
106
|
}
|
|
105
107
|
let pkg = Util_Bundle$ReventlessAws.extractPackageName(info.specModulePath);
|
|
106
108
|
packageDirs[pkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(pkg);
|
|
109
|
+
let bodyPkg = Util_Bundle$ReventlessAws.extractPackageName(info.bodyModulePath);
|
|
110
|
+
packageDirs[bodyPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(bodyPkg);
|
|
107
111
|
let specModule = Stdlib_Option.getOr(JSON.stringify(info.specModulePath), `""`);
|
|
112
|
+
let bodyModule = Stdlib_Option.getOr(JSON.stringify(info.bodyModulePath), `""`);
|
|
108
113
|
let callbackType = Stdlib_Option.getOr(JSON.stringify(info.callbackType), `""`);
|
|
114
|
+
let context = info.context;
|
|
115
|
+
let contextFragment = context !== undefined ? Stdlib_Option.getOr(Stdlib_Option.map(JSON.stringify(context), json => `,"context":` + json), "") : "";
|
|
109
116
|
let handlerJson = Pulumi.all([
|
|
110
117
|
info.queryDbTableName,
|
|
111
118
|
dcbQueueUrl,
|
|
112
119
|
spec.sourceUrns
|
|
113
120
|
]).apply(param => {
|
|
114
121
|
let sourceUrn = param[2][0];
|
|
115
|
-
return `{"specModule":` + specModule + `,"callbackType":` + callbackType + `,"queryDbTableName":"` + param[0] + `","dcbQueueUrl":"` + param[1] + `","sourceUrn":"` + sourceUrn + `"}`;
|
|
122
|
+
return `{"specModule":` + specModule + `,"bodyModule":` + bodyModule + `,"callbackType":` + callbackType + `,"queryDbTableName":"` + param[0] + `","dcbQueueUrl":"` + param[1] + `","sourceUrn":"` + sourceUrn + `"` + contextFragment + `}`;
|
|
116
123
|
});
|
|
117
124
|
handlerOutputs.push(handlerJson);
|
|
118
125
|
});
|